Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (377 commits) ASoC: au1x: PSC-AC97 bugfixes ALSA: dummy - Increase MAX_PCM_SUBSTREAMS to 128 ALSA: dummy - Add debug proc file ALSA: Add const prefix to proc helper functions ALSA: Re-export snd_pcm_format_name() function ALSA: hda - Use auto model for HP laptops with ALC268 codec ALSA: cs46xx - Fix minimum period size ASoC: Fix WM835x Out4 capture enumeration ALSA: Remove unneeded ifdef from sound/core.h ALSA: Remove struct snd_monitor_file from public sound/core.h ASoC: Remove unuused hw_read_t sound: oxygen: work around MCE when changing volume ALSA: dummy - Fake buffer allocations ALSA: hda/realtek: Added support for CLEVO M540R subsystem, 6 channel + digital ASoC: fix pxa2xx-ac97.c breakage ALSA: dummy - Fix the timer calculation in systimer mode ALSA: dummy - Add more description ALSA: dummy - Better jiffies handling ALSA: dummy - Support high-res timer mode ALSA: Release v1.0.21 ...
This commit is contained in:
		
				commit
				
					
						a9c86d4259
					
				
			
		
					 221 changed files with 30757 additions and 7017 deletions
				
			
		|  | @ -468,3 +468,27 @@ Why:	cpu_policy_rwsem has a new cleaner definition making it local to | |||
| 	cpufreq core and contained inside cpufreq.c. Other dependent | ||||
| 	drivers should not use it in order to safely avoid lockdep issues. | ||||
| Who:	Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | ||||
| 
 | ||||
| ---------------------------- | ||||
| 
 | ||||
| What:	sound-slot/service-* module aliases and related clutters in | ||||
| 	sound/sound_core.c | ||||
| When:	August 2010 | ||||
| Why:	OSS sound_core grabs all legacy minors (0-255) of SOUND_MAJOR | ||||
| 	(14) and requests modules using custom sound-slot/service-* | ||||
| 	module aliases.  The only benefit of doing this is allowing | ||||
| 	use of custom module aliases which might as well be considered | ||||
| 	a bug at this point.  This preemptive claiming prevents | ||||
| 	alternative OSS implementations. | ||||
| 
 | ||||
| 	Till the feature is removed, the kernel will be requesting | ||||
| 	both sound-slot/service-* and the standard char-major-* module | ||||
| 	aliases and allow turning off the pre-claiming selectively via | ||||
| 	CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss | ||||
| 	kernel parameter. | ||||
| 
 | ||||
| 	After the transition phase is complete, both the custom module | ||||
| 	aliases and switches to disable it will go away.  This removal | ||||
| 	will also allow making ALSA OSS emulation independent of | ||||
| 	sound_core.  The dependency will be broken then too. | ||||
| Who:	Tejun Heo <tj@kernel.org> | ||||
|  |  | |||
|  | @ -60,6 +60,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. | |||
|     slots	- Reserve the slot index for the given driver. | ||||
| 		  This option takes multiple strings.		 | ||||
| 		  See "Module Autoloading Support" section for details. | ||||
|     debug	- Specifies the debug message level | ||||
| 		  (0 = disable debug prints, 1 = normal debug messages, | ||||
| 		   2 = verbose debug messages) | ||||
| 		  This option appears only when CONFIG_SND_DEBUG=y. | ||||
| 		  This option can be dynamically changed via sysfs | ||||
| 		  /sys/modules/snd/parameters/debug file. | ||||
|    | ||||
|   Module snd-pcm-oss | ||||
|   ------------------ | ||||
|  | @ -513,6 +519,26 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. | |||
|     or input, but you may use this module for any application which | ||||
|     requires a sound card (like RealPlayer). | ||||
| 
 | ||||
|     pcm_devs       - Number of PCM devices assigned to each card | ||||
|                      (default = 1, up to 4) | ||||
|     pcm_substreams - Number of PCM substreams assigned to each PCM | ||||
|                      (default = 8, up to 16) | ||||
|     hrtimer        - Use hrtimer (=1, default) or system timer (=0) | ||||
|     fake_buffer    - Fake buffer allocations (default = 1) | ||||
| 
 | ||||
|     When multiple PCM devices are created, snd-dummy gives different | ||||
|     behavior to each PCM device: | ||||
|       0 = interleaved with mmap support | ||||
|       1 = non-interleaved with mmap support | ||||
|       2 = interleaved without mmap  | ||||
|       3 = non-interleaved without mmap | ||||
| 
 | ||||
|     As default, snd-dummy drivers doesn't allocate the real buffers | ||||
|     but either ignores read/write or mmap a single dummy page to all | ||||
|     buffer pages, in order to save the resouces.  If your apps need | ||||
|     the read/ written buffer data to be consistent, pass fake_buffer=0 | ||||
|     option. | ||||
| 
 | ||||
|     The power-management is supported. | ||||
| 
 | ||||
|   Module snd-echo3g | ||||
|  | @ -768,6 +794,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. | |||
|     bdl_pos_adj	- Specifies the DMA IRQ timing delay in samples. | ||||
| 		Passing -1 will make the driver to choose the appropriate | ||||
| 		value based on the controller chip. | ||||
|     patch	- Specifies the early "patch" files to modify the HD-audio | ||||
|     		setup before initializing the codecs.  This option is | ||||
| 		available only when CONFIG_SND_HDA_PATCH_LOADER=y is set. | ||||
| 		See HD-Audio.txt for details. | ||||
|      | ||||
|     [Single (global) options] | ||||
|     single_cmd  - Use single immediate commands to communicate with | ||||
|  |  | |||
|  | @ -114,8 +114,8 @@ ALC662/663/272 | |||
|   samsung-nc10	Samsung NC10 mini notebook | ||||
|   auto		auto-config reading BIOS (default) | ||||
| 
 | ||||
| ALC882/885 | ||||
| ========== | ||||
| ALC882/883/885/888/889 | ||||
| ====================== | ||||
|   3stack-dig	3-jack with SPDIF I/O | ||||
|   6stack-dig	6-jack digital with SPDIF I/O | ||||
|   arima		Arima W820Di1 | ||||
|  | @ -127,12 +127,8 @@ ALC882/885 | |||
|   mbp3		Macbook Pro rev3 | ||||
|   imac24	iMac 24'' with jack detection | ||||
|   w2jc		ASUS W2JC | ||||
|   auto		auto-config reading BIOS (default) | ||||
| 
 | ||||
| ALC883/888 | ||||
| ========== | ||||
|   3stack-dig	3-jack with SPDIF I/O | ||||
|   6stack-dig	6-jack digital with SPDIF I/O | ||||
|   3stack-2ch-dig	3-jack with SPDIF I/O (ALC883) | ||||
|   alc883-6stack-dig	6-jack digital with SPDIF I/O (ALC883) | ||||
|   3stack-6ch    3-jack 6-channel | ||||
|   3stack-6ch-dig 3-jack 6-channel with SPDIF I/O | ||||
|   6stack-dig-demo  6-jack digital for Intel demo board | ||||
|  | @ -140,6 +136,7 @@ ALC883/888 | |||
|   acer-aspire	Acer Aspire 9810 | ||||
|   acer-aspire-4930g Acer Aspire 4930G | ||||
|   acer-aspire-6530g Acer Aspire 6530G | ||||
|   acer-aspire-7730g Acer Aspire 7730G | ||||
|   acer-aspire-8930g Acer Aspire 8930G | ||||
|   medion	Medion Laptops | ||||
|   medion-md2	Medion MD2 | ||||
|  | @ -155,10 +152,13 @@ ALC883/888 | |||
|   3stack-hp	HP machines with 3stack (Lucknow, Samba boards) | ||||
|   6stack-dell	Dell machines with 6stack (Inspiron 530) | ||||
|   mitac		Mitac 8252D | ||||
|   clevo-m540r	Clevo M540R (6ch + digital) | ||||
|   clevo-m720	Clevo M720 laptop series | ||||
|   fujitsu-pi2515 Fujitsu AMILO Pi2515 | ||||
|   fujitsu-xa3530 Fujitsu AMILO XA3530 | ||||
|   3stack-6ch-intel Intel DG33* boards | ||||
|   intel-alc889a	Intel IbexPeak with ALC889A | ||||
|   intel-x58	Intel DX58 with ALC889 | ||||
|   asus-p5q	ASUS P5Q-EM boards | ||||
|   mb31		MacBook 3,1 | ||||
|   sony-vaio-tt  Sony VAIO TT | ||||
|  | @ -229,7 +229,7 @@ AD1984 | |||
| ====== | ||||
|   basic		default configuration | ||||
|   thinkpad	Lenovo Thinkpad T61/X61 | ||||
|   dell		Dell T3400 | ||||
|   dell_desktop	Dell T3400 | ||||
| 
 | ||||
| AD1986A | ||||
| ======= | ||||
|  | @ -258,6 +258,7 @@ Conexant 5045 | |||
|   laptop-micsense   Laptop with Mic sense (old model fujitsu) | ||||
|   laptop-hpmicsense Laptop with HP and Mic senses | ||||
|   benq		Benq R55E | ||||
|   laptop-hp530	HP 530 laptop | ||||
|   test		for testing/debugging purpose, almost all controls | ||||
| 		can be adjusted.  Appearing only when compiled with | ||||
| 		$CONFIG_SND_DEBUG=y | ||||
|  | @ -278,9 +279,16 @@ Conexant 5051 | |||
|   hp-dv6736	HP dv6736 | ||||
|   lenovo-x200	Lenovo X200 laptop | ||||
| 
 | ||||
| Conexant 5066 | ||||
| ============= | ||||
|   laptop	Basic Laptop config (default) | ||||
|   dell-laptop	Dell laptops | ||||
|   olpc-xo-1_5	OLPC XO 1.5 | ||||
| 
 | ||||
| STAC9200 | ||||
| ======== | ||||
|   ref		Reference board | ||||
|   oqo		OQO Model 2 | ||||
|   dell-d21	Dell (unknown) | ||||
|   dell-d22	Dell (unknown) | ||||
|   dell-d23	Dell (unknown) | ||||
|  | @ -368,10 +376,12 @@ STAC92HD73* | |||
| =========== | ||||
|   ref		Reference board | ||||
|   no-jd		BIOS setup but without jack-detection | ||||
|   intel		Intel DG45* mobos | ||||
|   dell-m6-amic	Dell desktops/laptops with analog mics | ||||
|   dell-m6-dmic	Dell desktops/laptops with digital mics | ||||
|   dell-m6	Dell desktops/laptops with both type of mics | ||||
|   dell-eq	Dell desktops/laptops | ||||
|   alienware	Alienware M17x | ||||
|   auto		BIOS setup (default) | ||||
| 
 | ||||
| STAC92HD83* | ||||
|  | @ -385,3 +395,8 @@ STAC9872 | |||
| ======== | ||||
|   vaio		VAIO laptop without SPDIF | ||||
|   auto		BIOS setup (default) | ||||
| 
 | ||||
| Cirrus Logic CS4206/4207 | ||||
| ======================== | ||||
|   mbp55		MacBook Pro 5,5 | ||||
|   auto		BIOS setup (default) | ||||
|  |  | |||
|  | @ -138,6 +138,10 @@ override the BIOS setup or to provide more comprehensive features. | |||
| The driver checks PCI SSID and looks through the static configuration | ||||
| table until any matching entry is found.  If you have a new machine, | ||||
| you may see a message like below: | ||||
| ------------------------------------------------------------------------ | ||||
|     hda_codec: ALC880: BIOS auto-probing. | ||||
| ------------------------------------------------------------------------ | ||||
| Meanwhile, in the earlier versions, you would see a message like: | ||||
| ------------------------------------------------------------------------ | ||||
|     hda_codec: Unknown model for ALC880, trying auto-probe from BIOS... | ||||
| ------------------------------------------------------------------------ | ||||
|  | @ -403,6 +407,66 @@ re-configure based on that state, run like below: | |||
| ------------------------------------------------------------------------ | ||||
| 
 | ||||
| 
 | ||||
| Early Patching | ||||
| ~~~~~~~~~~~~~~ | ||||
| When CONFIG_SND_HDA_PATCH_LOADER=y is set, you can pass a "patch" as a | ||||
| firmware file for modifying the HD-audio setup before initializing the | ||||
| codec.  This can work basically like the reconfiguration via sysfs in | ||||
| the above, but it does it before the first codec configuration. | ||||
| 
 | ||||
| A patch file is a plain text file which looks like below: | ||||
| 
 | ||||
| ------------------------------------------------------------------------ | ||||
|   [codec] | ||||
|   0x12345678 0xabcd1234 2 | ||||
| 
 | ||||
|   [model] | ||||
|   auto | ||||
| 
 | ||||
|   [pincfg] | ||||
|   0x12 0x411111f0 | ||||
| 
 | ||||
|   [verb] | ||||
|   0x20 0x500 0x03 | ||||
|   0x20 0x400 0xff | ||||
| 
 | ||||
|   [hint] | ||||
|   hp_detect = yes | ||||
| ------------------------------------------------------------------------ | ||||
| 
 | ||||
| The file needs to have a line `[codec]`.  The next line should contain | ||||
| three numbers indicating the codec vendor-id (0x12345678 in the | ||||
| example), the codec subsystem-id (0xabcd1234) and the address (2) of | ||||
| the codec.  The rest patch entries are applied to this specified codec | ||||
| until another codec entry is given. | ||||
| 
 | ||||
| The `[model]` line allows to change the model name of the each codec. | ||||
| In the example above, it will be changed to model=auto. | ||||
| Note that this overrides the module option. | ||||
| 
 | ||||
| After the `[pincfg]` line, the contents are parsed as the initial | ||||
| default pin-configurations just like `user_pin_configs` sysfs above. | ||||
| The values can be shown in user_pin_configs sysfs file, too. | ||||
| 
 | ||||
| Similarly, the lines after `[verb]` are parsed as `init_verbs` | ||||
| sysfs entries, and the lines after `[hint]` are parsed as `hints` | ||||
| sysfs entries, respectively. | ||||
| 
 | ||||
| The hd-audio driver reads the file via request_firmware().  Thus, | ||||
| a patch file has to be located on the appropriate firmware path, | ||||
| typically, /lib/firmware.  For example, when you pass the option | ||||
| `patch=hda-init.fw`, the file /lib/firmware/hda-init-fw must be | ||||
| present. | ||||
| 
 | ||||
| The patch module option is specific to each card instance, and you | ||||
| need to give one file name for each instance, separated by commas. | ||||
| For example, if you have two cards, one for an on-board analog and one  | ||||
| for an HDMI video board, you may pass patch option like below: | ||||
| ------------------------------------------------------------------------ | ||||
|     options snd-hda-intel patch=on-board-patch,hdmi-patch | ||||
| ------------------------------------------------------------------------ | ||||
| 
 | ||||
| 
 | ||||
| Power-Saving | ||||
| ~~~~~~~~~~~~ | ||||
| The power-saving is a kind of auto-suspend of the device.  When the | ||||
|  |  | |||
|  | @ -128,6 +128,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 		.rx_irq		= INT_24XX_MCBSP1_IRQ_RX, | ||||
| 		.tx_irq		= INT_24XX_MCBSP1_IRQ_TX, | ||||
| 		.ops		= &omap2_mcbsp_ops, | ||||
| 		.buffer_size	= 0x6F, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.phys_base	= OMAP34XX_MCBSP2_BASE, | ||||
|  | @ -136,6 +137,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 		.rx_irq		= INT_24XX_MCBSP2_IRQ_RX, | ||||
| 		.tx_irq		= INT_24XX_MCBSP2_IRQ_TX, | ||||
| 		.ops		= &omap2_mcbsp_ops, | ||||
| 		.buffer_size	= 0x3FF, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.phys_base	= OMAP34XX_MCBSP3_BASE, | ||||
|  | @ -144,6 +146,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 		.rx_irq		= INT_24XX_MCBSP3_IRQ_RX, | ||||
| 		.tx_irq		= INT_24XX_MCBSP3_IRQ_TX, | ||||
| 		.ops		= &omap2_mcbsp_ops, | ||||
| 		.buffer_size	= 0x6F, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.phys_base	= OMAP34XX_MCBSP4_BASE, | ||||
|  | @ -152,6 +155,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 		.rx_irq		= INT_24XX_MCBSP4_IRQ_RX, | ||||
| 		.tx_irq		= INT_24XX_MCBSP4_IRQ_TX, | ||||
| 		.ops		= &omap2_mcbsp_ops, | ||||
| 		.buffer_size	= 0x6F, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.phys_base	= OMAP34XX_MCBSP5_BASE, | ||||
|  | @ -160,6 +164,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 		.rx_irq		= INT_24XX_MCBSP5_IRQ_RX, | ||||
| 		.tx_irq		= INT_24XX_MCBSP5_IRQ_TX, | ||||
| 		.ops		= &omap2_mcbsp_ops, | ||||
| 		.buffer_size	= 0x6F, | ||||
| 	}, | ||||
| }; | ||||
| #define OMAP34XX_MCBSP_PDATA_SZ		ARRAY_SIZE(omap34xx_mcbsp_pdata) | ||||
|  |  | |||
|  | @ -3,10 +3,12 @@ | |||
| 
 | ||||
| #include <sound/core.h> | ||||
| #include <sound/pcm.h> | ||||
| #include <sound/ac97_codec.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * @reset_gpio: AC97 reset gpio (normally gpio113 or gpio95) | ||||
|  *              a -1 value means no gpio will be used for reset | ||||
|  * @codec_pdata: AC97 codec platform_data | ||||
| 
 | ||||
|  * reset_gpio should only be specified for pxa27x CPUs where a silicon | ||||
|  * bug prevents correct operation of the reset line. If not specified, | ||||
|  | @ -20,6 +22,7 @@ typedef struct { | |||
| 	void (*resume)(void *); | ||||
| 	void *priv; | ||||
| 	int reset_gpio; | ||||
| 	void *codec_pdata[AC97_BUS_MAX_DEVICES]; | ||||
| } pxa2xx_audio_ops_t; | ||||
| 
 | ||||
| extern void pxa_set_ac97_info(pxa2xx_audio_ops_t *ops); | ||||
|  |  | |||
|  | @ -1127,6 +1127,11 @@ int omap_dma_running(void) | |||
| void omap_dma_link_lch(int lch_head, int lch_queue) | ||||
| { | ||||
| 	if (omap_dma_in_1510_mode()) { | ||||
| 		if (lch_head == lch_queue) { | ||||
| 			dma_write(dma_read(CCR(lch_head)) | (3 << 8), | ||||
| 								CCR(lch_head)); | ||||
| 			return; | ||||
| 		} | ||||
| 		printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); | ||||
| 		BUG(); | ||||
| 		return; | ||||
|  | @ -1149,6 +1154,11 @@ EXPORT_SYMBOL(omap_dma_link_lch); | |||
| void omap_dma_unlink_lch(int lch_head, int lch_queue) | ||||
| { | ||||
| 	if (omap_dma_in_1510_mode()) { | ||||
| 		if (lch_head == lch_queue) { | ||||
| 			dma_write(dma_read(CCR(lch_head)) & ~(3 << 8), | ||||
| 								CCR(lch_head)); | ||||
| 			return; | ||||
| 		} | ||||
| 		printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); | ||||
| 		BUG(); | ||||
| 		return; | ||||
|  |  | |||
|  | @ -134,6 +134,11 @@ | |||
| #define OMAP_MCBSP_REG_XCERG	0x74 | ||||
| #define OMAP_MCBSP_REG_XCERH	0x78 | ||||
| #define OMAP_MCBSP_REG_SYSCON	0x8C | ||||
| #define OMAP_MCBSP_REG_THRSH2	0x90 | ||||
| #define OMAP_MCBSP_REG_THRSH1	0x94 | ||||
| #define OMAP_MCBSP_REG_IRQST	0xA0 | ||||
| #define OMAP_MCBSP_REG_IRQEN	0xA4 | ||||
| #define OMAP_MCBSP_REG_WAKEUPEN	0xA8 | ||||
| #define OMAP_MCBSP_REG_XCCR	0xAC | ||||
| #define OMAP_MCBSP_REG_RCCR	0xB0 | ||||
| 
 | ||||
|  | @ -249,8 +254,27 @@ | |||
| #define RDISABLE		0x0001 | ||||
| 
 | ||||
| /********************** McBSP SYSCONFIG bit definitions ********************/ | ||||
| #define CLOCKACTIVITY(value)	((value)<<8) | ||||
| #define SIDLEMODE(value)	((value)<<3) | ||||
| #define ENAWAKEUP		0x0004 | ||||
| #define SOFTRST			0x0002 | ||||
| 
 | ||||
| /********************** McBSP DMA operating modes **************************/ | ||||
| #define MCBSP_DMA_MODE_ELEMENT		0 | ||||
| #define MCBSP_DMA_MODE_THRESHOLD	1 | ||||
| #define MCBSP_DMA_MODE_FRAME		2 | ||||
| 
 | ||||
| /********************** McBSP WAKEUPEN bit definitions *********************/ | ||||
| #define XEMPTYEOFEN		0x4000 | ||||
| #define XRDYEN			0x0400 | ||||
| #define XEOFEN			0x0200 | ||||
| #define XFSXEN			0x0100 | ||||
| #define XSYNCERREN		0x0080 | ||||
| #define RRDYEN			0x0008 | ||||
| #define REOFEN			0x0004 | ||||
| #define RFSREN			0x0002 | ||||
| #define RSYNCERREN		0x0001 | ||||
| 
 | ||||
| /* we don't do multichannel for now */ | ||||
| struct omap_mcbsp_reg_cfg { | ||||
| 	u16 spcr2; | ||||
|  | @ -344,6 +368,9 @@ struct omap_mcbsp_platform_data { | |||
| 	u8 dma_rx_sync, dma_tx_sync; | ||||
| 	u16 rx_irq, tx_irq; | ||||
| 	struct omap_mcbsp_ops *ops; | ||||
| #ifdef CONFIG_ARCH_OMAP34XX | ||||
| 	u16 buffer_size; | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| struct omap_mcbsp { | ||||
|  | @ -377,6 +404,11 @@ struct omap_mcbsp { | |||
| 	struct omap_mcbsp_platform_data *pdata; | ||||
| 	struct clk *iclk; | ||||
| 	struct clk *fclk; | ||||
| #ifdef CONFIG_ARCH_OMAP34XX | ||||
| 	int dma_op_mode; | ||||
| 	u16 max_tx_thres; | ||||
| 	u16 max_rx_thres; | ||||
| #endif | ||||
| }; | ||||
| extern struct omap_mcbsp **mcbsp_ptr; | ||||
| extern int omap_mcbsp_count; | ||||
|  | @ -385,10 +417,25 @@ int omap_mcbsp_init(void); | |||
| void omap_mcbsp_register_board_cfg(struct omap_mcbsp_platform_data *config, | ||||
| 					int size); | ||||
| void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config); | ||||
| #ifdef CONFIG_ARCH_OMAP34XX | ||||
| void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold); | ||||
| void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold); | ||||
| u16 omap_mcbsp_get_max_tx_threshold(unsigned int id); | ||||
| u16 omap_mcbsp_get_max_rx_threshold(unsigned int id); | ||||
| int omap_mcbsp_get_dma_op_mode(unsigned int id); | ||||
| #else | ||||
| static inline void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold) | ||||
| { } | ||||
| static inline void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold) | ||||
| { } | ||||
| static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; } | ||||
| static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; } | ||||
| static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; } | ||||
| #endif | ||||
| int omap_mcbsp_request(unsigned int id); | ||||
| void omap_mcbsp_free(unsigned int id); | ||||
| void omap_mcbsp_start(unsigned int id); | ||||
| void omap_mcbsp_stop(unsigned int id); | ||||
| void omap_mcbsp_start(unsigned int id, int tx, int rx); | ||||
| void omap_mcbsp_stop(unsigned int id, int tx, int rx); | ||||
| void omap_mcbsp_xmit_word(unsigned int id, u32 word); | ||||
| u32 omap_mcbsp_recv_word(unsigned int id); | ||||
| 
 | ||||
|  |  | |||
|  | @ -198,6 +198,170 @@ void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config) | |||
| } | ||||
| EXPORT_SYMBOL(omap_mcbsp_config); | ||||
| 
 | ||||
| #ifdef CONFIG_ARCH_OMAP34XX | ||||
| /*
 | ||||
|  * omap_mcbsp_set_tx_threshold configures how to deal | ||||
|  * with transmit threshold. the threshold value and handler can be | ||||
|  * configure in here. | ||||
|  */ | ||||
| void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold) | ||||
| { | ||||
| 	struct omap_mcbsp *mcbsp; | ||||
| 	void __iomem *io_base; | ||||
| 
 | ||||
| 	if (!cpu_is_omap34xx()) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!omap_mcbsp_check_valid_id(id)) { | ||||
| 		printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | ||||
| 		return; | ||||
| 	} | ||||
| 	mcbsp = id_to_mcbsp_ptr(id); | ||||
| 	io_base = mcbsp->io_base; | ||||
| 
 | ||||
| 	OMAP_MCBSP_WRITE(io_base, THRSH2, threshold); | ||||
| } | ||||
| EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold); | ||||
| 
 | ||||
| /*
 | ||||
|  * omap_mcbsp_set_rx_threshold configures how to deal | ||||
|  * with receive threshold. the threshold value and handler can be | ||||
|  * configure in here. | ||||
|  */ | ||||
| void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold) | ||||
| { | ||||
| 	struct omap_mcbsp *mcbsp; | ||||
| 	void __iomem *io_base; | ||||
| 
 | ||||
| 	if (!cpu_is_omap34xx()) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!omap_mcbsp_check_valid_id(id)) { | ||||
| 		printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | ||||
| 		return; | ||||
| 	} | ||||
| 	mcbsp = id_to_mcbsp_ptr(id); | ||||
| 	io_base = mcbsp->io_base; | ||||
| 
 | ||||
| 	OMAP_MCBSP_WRITE(io_base, THRSH1, threshold); | ||||
| } | ||||
| EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold); | ||||
| 
 | ||||
| /*
 | ||||
|  * omap_mcbsp_get_max_tx_thres just return the current configured | ||||
|  * maximum threshold for transmission | ||||
|  */ | ||||
| u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) | ||||
| { | ||||
| 	struct omap_mcbsp *mcbsp; | ||||
| 
 | ||||
| 	if (!omap_mcbsp_check_valid_id(id)) { | ||||
| 		printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 	mcbsp = id_to_mcbsp_ptr(id); | ||||
| 
 | ||||
| 	return mcbsp->max_tx_thres; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold); | ||||
| 
 | ||||
| /*
 | ||||
|  * omap_mcbsp_get_max_rx_thres just return the current configured | ||||
|  * maximum threshold for reception | ||||
|  */ | ||||
| u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) | ||||
| { | ||||
| 	struct omap_mcbsp *mcbsp; | ||||
| 
 | ||||
| 	if (!omap_mcbsp_check_valid_id(id)) { | ||||
| 		printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 	mcbsp = id_to_mcbsp_ptr(id); | ||||
| 
 | ||||
| 	return mcbsp->max_rx_thres; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold); | ||||
| 
 | ||||
| /*
 | ||||
|  * omap_mcbsp_get_dma_op_mode just return the current configured | ||||
|  * operating mode for the mcbsp channel | ||||
|  */ | ||||
| int omap_mcbsp_get_dma_op_mode(unsigned int id) | ||||
| { | ||||
| 	struct omap_mcbsp *mcbsp; | ||||
| 	int dma_op_mode; | ||||
| 
 | ||||
| 	if (!omap_mcbsp_check_valid_id(id)) { | ||||
| 		printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 	mcbsp = id_to_mcbsp_ptr(id); | ||||
| 
 | ||||
| 	spin_lock_irq(&mcbsp->lock); | ||||
| 	dma_op_mode = mcbsp->dma_op_mode; | ||||
| 	spin_unlock_irq(&mcbsp->lock); | ||||
| 
 | ||||
| 	return dma_op_mode; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode); | ||||
| 
 | ||||
| static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Enable wakup behavior, smart idle and all wakeups | ||||
| 	 * REVISIT: some wakeups may be unnecessary | ||||
| 	 */ | ||||
| 	if (cpu_is_omap34xx()) { | ||||
| 		u16 syscon; | ||||
| 
 | ||||
| 		syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON); | ||||
| 		syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03)); | ||||
| 
 | ||||
| 		spin_lock_irq(&mcbsp->lock); | ||||
| 		if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { | ||||
| 			syscon |= (ENAWAKEUP | SIDLEMODE(0x02) | | ||||
| 					CLOCKACTIVITY(0x02)); | ||||
| 			OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN, | ||||
| 					XRDYEN | RRDYEN); | ||||
| 		} else { | ||||
| 			syscon |= SIDLEMODE(0x01); | ||||
| 		} | ||||
| 		spin_unlock_irq(&mcbsp->lock); | ||||
| 
 | ||||
| 		OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Disable wakup behavior, smart idle and all wakeups | ||||
| 	 */ | ||||
| 	if (cpu_is_omap34xx()) { | ||||
| 		u16 syscon; | ||||
| 
 | ||||
| 		syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON); | ||||
| 		syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03)); | ||||
| 		/*
 | ||||
| 		 * HW bug workaround - If no_idle mode is taken, we need to | ||||
| 		 * go to smart_idle before going to always_idle, or the | ||||
| 		 * device will not hit retention anymore. | ||||
| 		 */ | ||||
| 		syscon |= SIDLEMODE(0x02); | ||||
| 		OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon); | ||||
| 
 | ||||
| 		syscon &= ~(SIDLEMODE(0x03)); | ||||
| 		OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon); | ||||
| 
 | ||||
| 		OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN, 0); | ||||
| 	} | ||||
| } | ||||
| #else | ||||
| static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {} | ||||
| static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {} | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * We can choose between IRQ based or polled IO. | ||||
|  * This needs to be called before omap_mcbsp_request(). | ||||
|  | @ -257,6 +421,9 @@ int omap_mcbsp_request(unsigned int id) | |||
| 	clk_enable(mcbsp->iclk); | ||||
| 	clk_enable(mcbsp->fclk); | ||||
| 
 | ||||
| 	/* Do procedure specific to omap34xx arch, if applicable */ | ||||
| 	omap34xx_mcbsp_request(mcbsp); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Make sure that transmitter, receiver and sample-rate generator are | ||||
| 	 * not running before activating IRQs. | ||||
|  | @ -305,6 +472,9 @@ void omap_mcbsp_free(unsigned int id) | |||
| 	if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) | ||||
| 		mcbsp->pdata->ops->free(id); | ||||
| 
 | ||||
| 	/* Do procedure specific to omap34xx arch, if applicable */ | ||||
| 	omap34xx_mcbsp_free(mcbsp); | ||||
| 
 | ||||
| 	clk_disable(mcbsp->fclk); | ||||
| 	clk_disable(mcbsp->iclk); | ||||
| 
 | ||||
|  | @ -328,14 +498,15 @@ void omap_mcbsp_free(unsigned int id) | |||
| EXPORT_SYMBOL(omap_mcbsp_free); | ||||
| 
 | ||||
| /*
 | ||||
|  * Here we start the McBSP, by enabling the sample | ||||
|  * generator, both transmitter and receivers, | ||||
|  * and the frame sync. | ||||
|  * Here we start the McBSP, by enabling transmitter, receiver or both. | ||||
|  * If no transmitter or receiver is active prior calling, then sample-rate | ||||
|  * generator and frame sync are started. | ||||
|  */ | ||||
| void omap_mcbsp_start(unsigned int id) | ||||
| void omap_mcbsp_start(unsigned int id, int tx, int rx) | ||||
| { | ||||
| 	struct omap_mcbsp *mcbsp; | ||||
| 	void __iomem *io_base; | ||||
| 	int idle; | ||||
| 	u16 w; | ||||
| 
 | ||||
| 	if (!omap_mcbsp_check_valid_id(id)) { | ||||
|  | @ -348,32 +519,58 @@ void omap_mcbsp_start(unsigned int id) | |||
| 	mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7; | ||||
| 	mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7; | ||||
| 
 | ||||
| 	/* Start the sample generator */ | ||||
| 	w = OMAP_MCBSP_READ(io_base, SPCR2); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | ||||
| 	idle = !((OMAP_MCBSP_READ(io_base, SPCR2) | | ||||
| 		  OMAP_MCBSP_READ(io_base, SPCR1)) & 1); | ||||
| 
 | ||||
| 	if (idle) { | ||||
| 		/* Start the sample generator */ | ||||
| 		w = OMAP_MCBSP_READ(io_base, SPCR2); | ||||
| 		OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Enable transmitter and receiver */ | ||||
| 	tx &= 1; | ||||
| 	w = OMAP_MCBSP_READ(io_base, SPCR2); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR2, w | tx); | ||||
| 
 | ||||
| 	rx &= 1; | ||||
| 	w = OMAP_MCBSP_READ(io_base, SPCR1); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR1, w | rx); | ||||
| 
 | ||||
| 	udelay(100); | ||||
| 	/*
 | ||||
| 	 * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec | ||||
| 	 * REVISIT: 100us may give enough time for two CLKSRG, however | ||||
| 	 * due to some unknown PM related, clock gating etc. reason it | ||||
| 	 * is now at 500us. | ||||
| 	 */ | ||||
| 	udelay(500); | ||||
| 
 | ||||
| 	/* Start frame sync */ | ||||
| 	w = OMAP_MCBSP_READ(io_base, SPCR2); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | ||||
| 	if (idle) { | ||||
| 		/* Start frame sync */ | ||||
| 		w = OMAP_MCBSP_READ(io_base, SPCR2); | ||||
| 		OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | ||||
| 	} | ||||
| 
 | ||||
| 	if (cpu_is_omap2430() || cpu_is_omap34xx()) { | ||||
| 		/* Release the transmitter and receiver */ | ||||
| 		w = OMAP_MCBSP_READ(io_base, XCCR); | ||||
| 		w &= ~(tx ? XDISABLE : 0); | ||||
| 		OMAP_MCBSP_WRITE(io_base, XCCR, w); | ||||
| 		w = OMAP_MCBSP_READ(io_base, RCCR); | ||||
| 		w &= ~(rx ? RDISABLE : 0); | ||||
| 		OMAP_MCBSP_WRITE(io_base, RCCR, w); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Dump McBSP Regs */ | ||||
| 	omap_mcbsp_dump_reg(id); | ||||
| } | ||||
| EXPORT_SYMBOL(omap_mcbsp_start); | ||||
| 
 | ||||
| void omap_mcbsp_stop(unsigned int id) | ||||
| void omap_mcbsp_stop(unsigned int id, int tx, int rx) | ||||
| { | ||||
| 	struct omap_mcbsp *mcbsp; | ||||
| 	void __iomem *io_base; | ||||
| 	int idle; | ||||
| 	u16 w; | ||||
| 
 | ||||
| 	if (!omap_mcbsp_check_valid_id(id)) { | ||||
|  | @ -385,16 +582,33 @@ void omap_mcbsp_stop(unsigned int id) | |||
| 	io_base = mcbsp->io_base; | ||||
| 
 | ||||
| 	/* Reset transmitter */ | ||||
| 	tx &= 1; | ||||
| 	if (cpu_is_omap2430() || cpu_is_omap34xx()) { | ||||
| 		w = OMAP_MCBSP_READ(io_base, XCCR); | ||||
| 		w |= (tx ? XDISABLE : 0); | ||||
| 		OMAP_MCBSP_WRITE(io_base, XCCR, w); | ||||
| 	} | ||||
| 	w = OMAP_MCBSP_READ(io_base, SPCR2); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~tx); | ||||
| 
 | ||||
| 	/* Reset receiver */ | ||||
| 	rx &= 1; | ||||
| 	if (cpu_is_omap2430() || cpu_is_omap34xx()) { | ||||
| 		w = OMAP_MCBSP_READ(io_base, RCCR); | ||||
| 		w |= (tx ? RDISABLE : 0); | ||||
| 		OMAP_MCBSP_WRITE(io_base, RCCR, w); | ||||
| 	} | ||||
| 	w = OMAP_MCBSP_READ(io_base, SPCR1); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~rx); | ||||
| 
 | ||||
| 	/* Reset the sample rate generator */ | ||||
| 	w = OMAP_MCBSP_READ(io_base, SPCR2); | ||||
| 	OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | ||||
| 	idle = !((OMAP_MCBSP_READ(io_base, SPCR2) | | ||||
| 		  OMAP_MCBSP_READ(io_base, SPCR1)) & 1); | ||||
| 
 | ||||
| 	if (idle) { | ||||
| 		/* Reset the sample rate generator */ | ||||
| 		w = OMAP_MCBSP_READ(io_base, SPCR2); | ||||
| 		OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | ||||
| 	} | ||||
| } | ||||
| EXPORT_SYMBOL(omap_mcbsp_stop); | ||||
| 
 | ||||
|  | @ -883,6 +1097,149 @@ void omap_mcbsp_set_spi_mode(unsigned int id, | |||
| } | ||||
| EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); | ||||
| 
 | ||||
| #ifdef CONFIG_ARCH_OMAP34XX | ||||
| #define max_thres(m)			(mcbsp->pdata->buffer_size) | ||||
| #define valid_threshold(m, val)		((val) <= max_thres(m)) | ||||
| #define THRESHOLD_PROP_BUILDER(prop)					\ | ||||
| static ssize_t prop##_show(struct device *dev,				\ | ||||
| 			struct device_attribute *attr, char *buf)	\ | ||||
| {									\ | ||||
| 	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);		\ | ||||
| 									\ | ||||
| 	return sprintf(buf, "%u\n", mcbsp->prop);			\ | ||||
| }									\ | ||||
| 									\ | ||||
| static ssize_t prop##_store(struct device *dev,				\ | ||||
| 				struct device_attribute *attr,		\ | ||||
| 				const char *buf, size_t size)		\ | ||||
| {									\ | ||||
| 	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);		\ | ||||
| 	unsigned long val;						\ | ||||
| 	int status;							\ | ||||
| 									\ | ||||
| 	status = strict_strtoul(buf, 0, &val);				\ | ||||
| 	if (status)							\ | ||||
| 		return status;						\ | ||||
| 									\ | ||||
| 	if (!valid_threshold(mcbsp, val))				\ | ||||
| 		return -EDOM;						\ | ||||
| 									\ | ||||
| 	mcbsp->prop = val;						\ | ||||
| 	return size;							\ | ||||
| }									\ | ||||
| 									\ | ||||
| static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store); | ||||
| 
 | ||||
| THRESHOLD_PROP_BUILDER(max_tx_thres); | ||||
| THRESHOLD_PROP_BUILDER(max_rx_thres); | ||||
| 
 | ||||
| static const char *dma_op_modes[] = { | ||||
| 	"element", "threshold", "frame", | ||||
| }; | ||||
| 
 | ||||
| static ssize_t dma_op_mode_show(struct device *dev, | ||||
| 			struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||||
| 	int dma_op_mode, i = 0; | ||||
| 	ssize_t len = 0; | ||||
| 	const char * const *s; | ||||
| 
 | ||||
| 	spin_lock_irq(&mcbsp->lock); | ||||
| 	dma_op_mode = mcbsp->dma_op_mode; | ||||
| 	spin_unlock_irq(&mcbsp->lock); | ||||
| 
 | ||||
| 	for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { | ||||
| 		if (dma_op_mode == i) | ||||
| 			len += sprintf(buf + len, "[%s] ", *s); | ||||
| 		else | ||||
| 			len += sprintf(buf + len, "%s ", *s); | ||||
| 	} | ||||
| 	len += sprintf(buf + len, "\n"); | ||||
| 
 | ||||
| 	return len; | ||||
| } | ||||
| 
 | ||||
| static ssize_t dma_op_mode_store(struct device *dev, | ||||
| 				struct device_attribute *attr, | ||||
| 				const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); | ||||
| 	const char * const *s; | ||||
| 	int i = 0; | ||||
| 
 | ||||
| 	for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) | ||||
| 		if (sysfs_streq(buf, *s)) | ||||
| 			break; | ||||
| 
 | ||||
| 	if (i == ARRAY_SIZE(dma_op_modes)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	spin_lock_irq(&mcbsp->lock); | ||||
| 	if (!mcbsp->free) { | ||||
| 		size = -EBUSY; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 	mcbsp->dma_op_mode = i; | ||||
| 
 | ||||
| unlock: | ||||
| 	spin_unlock_irq(&mcbsp->lock); | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store); | ||||
| 
 | ||||
| static const struct attribute *additional_attrs[] = { | ||||
| 	&dev_attr_max_tx_thres.attr, | ||||
| 	&dev_attr_max_rx_thres.attr, | ||||
| 	&dev_attr_dma_op_mode.attr, | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| static const struct attribute_group additional_attr_group = { | ||||
| 	.attrs = (struct attribute **)additional_attrs, | ||||
| }; | ||||
| 
 | ||||
| static inline int __devinit omap_additional_add(struct device *dev) | ||||
| { | ||||
| 	return sysfs_create_group(&dev->kobj, &additional_attr_group); | ||||
| } | ||||
| 
 | ||||
| static inline void __devexit omap_additional_remove(struct device *dev) | ||||
| { | ||||
| 	sysfs_remove_group(&dev->kobj, &additional_attr_group); | ||||
| } | ||||
| 
 | ||||
| static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) | ||||
| { | ||||
| 	mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; | ||||
| 	if (cpu_is_omap34xx()) { | ||||
| 		mcbsp->max_tx_thres = max_thres(mcbsp); | ||||
| 		mcbsp->max_rx_thres = max_thres(mcbsp); | ||||
| 		/*
 | ||||
| 		 * REVISIT: Set dmap_op_mode to THRESHOLD as default | ||||
| 		 * for mcbsp2 instances. | ||||
| 		 */ | ||||
| 		if (omap_additional_add(mcbsp->dev)) | ||||
| 			dev_warn(mcbsp->dev, | ||||
| 				"Unable to create additional controls\n"); | ||||
| 	} else { | ||||
| 		mcbsp->max_tx_thres = -EINVAL; | ||||
| 		mcbsp->max_rx_thres = -EINVAL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) | ||||
| { | ||||
| 	if (cpu_is_omap34xx()) | ||||
| 		omap_additional_remove(mcbsp->dev); | ||||
| } | ||||
| #else | ||||
| static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {} | ||||
| static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {} | ||||
| #endif /* CONFIG_ARCH_OMAP34XX */ | ||||
| 
 | ||||
| /*
 | ||||
|  * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. | ||||
|  * 730 has only 2 McBSP, and both of them are MPU peripherals. | ||||
|  | @ -953,6 +1310,10 @@ static int __devinit omap_mcbsp_probe(struct platform_device *pdev) | |||
| 	mcbsp->dev = &pdev->dev; | ||||
| 	mcbsp_ptr[id] = mcbsp; | ||||
| 	platform_set_drvdata(pdev, mcbsp); | ||||
| 
 | ||||
| 	/* Initialize mcbsp properties for OMAP34XX if needed / applicable */ | ||||
| 	omap34xx_device_init(mcbsp); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_fclk: | ||||
|  | @ -976,6 +1337,8 @@ static int __devexit omap_mcbsp_remove(struct platform_device *pdev) | |||
| 				mcbsp->pdata->ops->free) | ||||
| 			mcbsp->pdata->ops->free(mcbsp->id); | ||||
| 
 | ||||
| 		omap34xx_device_exit(mcbsp); | ||||
| 
 | ||||
| 		clk_disable(mcbsp->fclk); | ||||
| 		clk_disable(mcbsp->iclk); | ||||
| 		clk_put(mcbsp->fclk); | ||||
|  |  | |||
							
								
								
									
										37
									
								
								arch/arm/plat-s3c/include/plat/audio-simtec.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								arch/arm/plat-s3c/include/plat/audio-simtec.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| /* arch/arm/plat-s3c/include/plat/audio-simtec.h
 | ||||
|  * | ||||
|  * Copyright 2008 Simtec Electronics | ||||
|  *	http://armlinux.simtec.co.uk/
 | ||||
|  *	Ben Dooks <ben@simtec.co.uk> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * Simtec Audio support. | ||||
| */ | ||||
| 
 | ||||
| /**
 | ||||
|  * struct s3c24xx_audio_simtec_pdata - platform data for simtec audio | ||||
|  * @use_mpllin: Select codec clock from MPLLin | ||||
|  * @output_cdclk: Need to output CDCLK to the codec | ||||
|  * @have_mic: Set if we have a MIC socket | ||||
|  * @have_lout: Set if we have a LineOut socket | ||||
|  * @amp_gpio: GPIO pin to enable the AMP | ||||
|  * @amp_gain: Option GPIO to control AMP gain | ||||
|  */ | ||||
| struct s3c24xx_audio_simtec_pdata { | ||||
| 	unsigned int	use_mpllin:1; | ||||
| 	unsigned int	output_cdclk:1; | ||||
| 
 | ||||
| 	unsigned int	have_mic:1; | ||||
| 	unsigned int	have_lout:1; | ||||
| 
 | ||||
| 	int		amp_gpio; | ||||
| 	int		amp_gain[2]; | ||||
| 
 | ||||
| 	void	(*startup)(void); | ||||
| }; | ||||
| 
 | ||||
| extern int simtec_audio_add(const char *codec_name, | ||||
| 			    struct s3c24xx_audio_simtec_pdata *pdata); | ||||
|  | @ -33,6 +33,11 @@ | |||
| #define S3C2412_IISCON_RXDMA_ACTIVE	(1 << 1) | ||||
| #define S3C2412_IISCON_IIS_ACTIVE	(1 << 0) | ||||
| 
 | ||||
| #define S3C64XX_IISMOD_BLC_16BIT	(0 << 13) | ||||
| #define S3C64XX_IISMOD_BLC_8BIT		(1 << 13) | ||||
| #define S3C64XX_IISMOD_BLC_24BIT	(2 << 13) | ||||
| #define S3C64XX_IISMOD_BLC_MASK		(3 << 13) | ||||
| 
 | ||||
| #define S3C64XX_IISMOD_IMS_PCLK		(0 << 10) | ||||
| #define S3C64XX_IISMOD_IMS_SYSMUX	(1 << 10) | ||||
| 
 | ||||
|  |  | |||
|  | @ -238,8 +238,10 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, | |||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * register_chrdev() - Register a major number for character devices. | ||||
|  * __register_chrdev() - create and register a cdev occupying a range of minors | ||||
|  * @major: major device number or 0 for dynamic allocation | ||||
|  * @baseminor: first of the requested range of minor numbers | ||||
|  * @count: the number of minor numbers required | ||||
|  * @name: name of this range of devices | ||||
|  * @fops: file operations associated with this devices | ||||
|  * | ||||
|  | @ -255,19 +257,17 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, | |||
|  * /dev. It only helps to keep track of the different owners of devices. If | ||||
|  * your module name has only one type of devices it's ok to use e.g. the name | ||||
|  * of the module here. | ||||
|  * | ||||
|  * This function registers a range of 256 minor numbers. The first minor number | ||||
|  * is 0. | ||||
|  */ | ||||
| int register_chrdev(unsigned int major, const char *name, | ||||
| 		    const struct file_operations *fops) | ||||
| int __register_chrdev(unsigned int major, unsigned int baseminor, | ||||
| 		      unsigned int count, const char *name, | ||||
| 		      const struct file_operations *fops) | ||||
| { | ||||
| 	struct char_device_struct *cd; | ||||
| 	struct cdev *cdev; | ||||
| 	char *s; | ||||
| 	int err = -ENOMEM; | ||||
| 
 | ||||
| 	cd = __register_chrdev_region(major, 0, 256, name); | ||||
| 	cd = __register_chrdev_region(major, baseminor, count, name); | ||||
| 	if (IS_ERR(cd)) | ||||
| 		return PTR_ERR(cd); | ||||
| 	 | ||||
|  | @ -281,7 +281,7 @@ int register_chrdev(unsigned int major, const char *name, | |||
| 	for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/')) | ||||
| 		*s = '!'; | ||||
| 		 | ||||
| 	err = cdev_add(cdev, MKDEV(cd->major, 0), 256); | ||||
| 	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count); | ||||
| 	if (err) | ||||
| 		goto out; | ||||
| 
 | ||||
|  | @ -291,7 +291,7 @@ int register_chrdev(unsigned int major, const char *name, | |||
| out: | ||||
| 	kobject_put(&cdev->kobj); | ||||
| out2: | ||||
| 	kfree(__unregister_chrdev_region(cd->major, 0, 256)); | ||||
| 	kfree(__unregister_chrdev_region(cd->major, baseminor, count)); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
|  | @ -317,10 +317,23 @@ void unregister_chrdev_region(dev_t from, unsigned count) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void unregister_chrdev(unsigned int major, const char *name) | ||||
| /**
 | ||||
|  * __unregister_chrdev - unregister and destroy a cdev | ||||
|  * @major: major device number | ||||
|  * @baseminor: first of the range of minor numbers | ||||
|  * @count: the number of minor numbers this cdev is occupying | ||||
|  * @name: name of this range of devices | ||||
|  * | ||||
|  * Unregister and destroy the cdev occupying the region described by | ||||
|  * @major, @baseminor and @count.  This function undoes what | ||||
|  * __register_chrdev() did. | ||||
|  */ | ||||
| void __unregister_chrdev(unsigned int major, unsigned int baseminor, | ||||
| 			 unsigned int count, const char *name) | ||||
| { | ||||
| 	struct char_device_struct *cd; | ||||
| 	cd = __unregister_chrdev_region(major, 0, 256); | ||||
| 
 | ||||
| 	cd = __unregister_chrdev_region(major, baseminor, count); | ||||
| 	if (cd && cd->cdev) | ||||
| 		cdev_del(cd->cdev); | ||||
| 	kfree(cd); | ||||
|  | @ -569,6 +582,6 @@ EXPORT_SYMBOL(cdev_alloc); | |||
| EXPORT_SYMBOL(cdev_del); | ||||
| EXPORT_SYMBOL(cdev_add); | ||||
| EXPORT_SYMBOL(cdev_index); | ||||
| EXPORT_SYMBOL(register_chrdev); | ||||
| EXPORT_SYMBOL(unregister_chrdev); | ||||
| EXPORT_SYMBOL(__register_chrdev); | ||||
| EXPORT_SYMBOL(__unregister_chrdev); | ||||
| EXPORT_SYMBOL(directly_mappable_cdev_bdi); | ||||
|  |  | |||
|  | @ -1997,12 +1997,25 @@ extern void bd_release_from_disk(struct block_device *, struct gendisk *); | |||
| #define CHRDEV_MAJOR_HASH_SIZE	255 | ||||
| extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); | ||||
| extern int register_chrdev_region(dev_t, unsigned, const char *); | ||||
| extern int register_chrdev(unsigned int, const char *, | ||||
| 			   const struct file_operations *); | ||||
| extern void unregister_chrdev(unsigned int, const char *); | ||||
| extern int __register_chrdev(unsigned int major, unsigned int baseminor, | ||||
| 			     unsigned int count, const char *name, | ||||
| 			     const struct file_operations *fops); | ||||
| extern void __unregister_chrdev(unsigned int major, unsigned int baseminor, | ||||
| 				unsigned int count, const char *name); | ||||
| extern void unregister_chrdev_region(dev_t, unsigned); | ||||
| extern void chrdev_show(struct seq_file *,off_t); | ||||
| 
 | ||||
| static inline int register_chrdev(unsigned int major, const char *name, | ||||
| 				  const struct file_operations *fops) | ||||
| { | ||||
| 	return __register_chrdev(major, 0, 256, name, fops); | ||||
| } | ||||
| 
 | ||||
| static inline void unregister_chrdev(unsigned int major, const char *name) | ||||
| { | ||||
| 	__unregister_chrdev(major, 0, 256, name); | ||||
| } | ||||
| 
 | ||||
| /* fs/block_dev.c */ | ||||
| #define BDEVNAME_SIZE	32	/* Largest string for a blockdev identifier */ | ||||
| #define BDEVT_SIZE	10	/* Largest string for MAJ:MIN for blkdev */ | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ | |||
|  */ | ||||
| #define NR_UNIX98_PTY_DEFAULT	4096      /* Default maximum for Unix98 ptys */ | ||||
| #define NR_UNIX98_PTY_MAX	(1 << MINORBITS) /* Absolute limit */ | ||||
| #define NR_LDISCS		19 | ||||
| #define NR_LDISCS		20 | ||||
| 
 | ||||
| /* line disciplines */ | ||||
| #define N_TTY		0 | ||||
|  | @ -47,6 +47,8 @@ | |||
| #define N_SLCAN		17	/* Serial / USB serial CAN Adaptors */ | ||||
| #define N_PPS		18	/* Pulse per Second */ | ||||
| 
 | ||||
| #define N_V253		19	/* Codec control over voice modem */ | ||||
| 
 | ||||
| /*
 | ||||
|  * This character is the same as _POSIX_VDISABLE: it cannot be used as | ||||
|  * a c_cc[] character, but indicates that a particular special character | ||||
|  |  | |||
|  | @ -32,6 +32,9 @@ | |||
| #include "control.h" | ||||
| #include "info.h" | ||||
| 
 | ||||
| /* maximum number of devices on the AC97 bus */ | ||||
| #define	AC97_BUS_MAX_DEVICES	4 | ||||
| 
 | ||||
| /*
 | ||||
|  *  AC'97 codec registers | ||||
|  */ | ||||
|  | @ -642,4 +645,10 @@ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime); | |||
| /* ad hoc AC97 device driver access */ | ||||
| extern struct bus_type ac97_bus_type; | ||||
| 
 | ||||
| /* AC97 platform_data adding function */ | ||||
| static inline void snd_ac97_dev_add_pdata(struct snd_ac97 *ac97, void *data) | ||||
| { | ||||
| 	ac97->dev.platform_data = data; | ||||
| } | ||||
| 
 | ||||
| #endif /* __SOUND_AC97_CODEC_H */ | ||||
|  |  | |||
|  | @ -138,7 +138,7 @@ struct snd_hwdep_dsp_image { | |||
|  *                                                                           * | ||||
|  *****************************************************************************/ | ||||
| 
 | ||||
| #define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 9) | ||||
| #define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 10) | ||||
| 
 | ||||
| typedef unsigned long snd_pcm_uframes_t; | ||||
| typedef signed long snd_pcm_sframes_t; | ||||
|  |  | |||
|  | @ -93,15 +93,6 @@ struct snd_device { | |||
| 
 | ||||
| #define snd_device(n) list_entry(n, struct snd_device, list) | ||||
| 
 | ||||
| /* monitor files for graceful shutdown (hotplug) */ | ||||
| 
 | ||||
| struct snd_monitor_file { | ||||
| 	struct file *file; | ||||
| 	const struct file_operations *disconnected_f_op; | ||||
| 	struct list_head shutdown_list;	/* still need to shutdown */ | ||||
| 	struct list_head list;	/* link of monitor files */ | ||||
| }; | ||||
| 
 | ||||
| /* main structure for soundcard */ | ||||
| 
 | ||||
| struct snd_card { | ||||
|  | @ -311,9 +302,7 @@ int snd_component_add(struct snd_card *card, const char *component); | |||
| int snd_card_file_add(struct snd_card *card, struct file *file); | ||||
| int snd_card_file_remove(struct snd_card *card, struct file *file); | ||||
| 
 | ||||
| #ifndef snd_card_set_dev | ||||
| #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr)) | ||||
| #endif | ||||
| 
 | ||||
| /* device.c */ | ||||
| 
 | ||||
|  | @ -340,18 +329,17 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size); | |||
| struct resource; | ||||
| void release_and_free_resource(struct resource *res); | ||||
| 
 | ||||
| #ifdef CONFIG_SND_VERBOSE_PRINTK | ||||
| void snd_verbose_printk(const char *file, int line, const char *format, ...) | ||||
|      __attribute__ ((format (printf, 3, 4))); | ||||
| #endif | ||||
| #if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) | ||||
| void snd_verbose_printd(const char *file, int line, const char *format, ...) | ||||
|      __attribute__ ((format (printf, 3, 4))); | ||||
| #endif | ||||
| 
 | ||||
| /* --- */ | ||||
| 
 | ||||
| #ifdef CONFIG_SND_VERBOSE_PRINTK | ||||
| #if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK) | ||||
| void __snd_printk(unsigned int level, const char *file, int line, | ||||
| 		  const char *format, ...) | ||||
|      __attribute__ ((format (printf, 4, 5))); | ||||
| #else | ||||
| #define __snd_printk(level, file, line, format, args...) \ | ||||
| 	printk(format, ##args) | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * snd_printk - printk wrapper | ||||
|  * @fmt: format string | ||||
|  | @ -360,15 +348,9 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) | |||
|  * when configured with CONFIG_SND_VERBOSE_PRINTK. | ||||
|  */ | ||||
| #define snd_printk(fmt, args...) \ | ||||
| 	snd_verbose_printk(__FILE__, __LINE__, fmt ,##args) | ||||
| #else | ||||
| #define snd_printk(fmt, args...) \ | ||||
| 	printk(fmt ,##args) | ||||
| #endif | ||||
| 	__snd_printk(0, __FILE__, __LINE__, fmt, ##args) | ||||
| 
 | ||||
| #ifdef CONFIG_SND_DEBUG | ||||
| 
 | ||||
| #ifdef CONFIG_SND_VERBOSE_PRINTK | ||||
| /**
 | ||||
|  * snd_printd - debug printk | ||||
|  * @fmt: format string | ||||
|  | @ -377,11 +359,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) | |||
|  * Ignored when CONFIG_SND_DEBUG is not set. | ||||
|  */ | ||||
| #define snd_printd(fmt, args...) \ | ||||
| 	snd_verbose_printd(__FILE__, __LINE__, fmt ,##args) | ||||
| #else | ||||
| #define snd_printd(fmt, args...) \ | ||||
| 	printk(fmt ,##args) | ||||
| #endif | ||||
| 	__snd_printk(1, __FILE__, __LINE__, fmt, ##args) | ||||
| 
 | ||||
| /**
 | ||||
|  * snd_BUG - give a BUG warning message and stack trace | ||||
|  | @ -428,9 +406,10 @@ static inline int __snd_bug_on(int cond) | |||
|  * Works like snd_printk() for debugging purposes. | ||||
|  * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set. | ||||
|  */ | ||||
| #define snd_printdd(format, args...) snd_printk(format, ##args) | ||||
| #define snd_printdd(format, args...) \ | ||||
| 	__snd_printk(2, __FILE__, __LINE__, format, ##args) | ||||
| #else | ||||
| #define snd_printdd(format, args...) /* nothing */ | ||||
| #define snd_printdd(format, args...)	do { } while (0) | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
|  | @ -438,12 +417,10 @@ static inline int __snd_bug_on(int cond) | |||
| 
 | ||||
| /* for easier backward-porting */ | ||||
| #if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) | ||||
| #ifndef gameport_set_dev_parent | ||||
| #define gameport_set_dev_parent(gp,xdev) ((gp)->dev.parent = (xdev)) | ||||
| #define gameport_set_port_data(gp,r) ((gp)->port_data = (r)) | ||||
| #define gameport_get_port_data(gp) (gp)->port_data | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| /* PCI quirk list helper */ | ||||
| struct snd_pci_quirk { | ||||
|  |  | |||
|  | @ -110,13 +110,13 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer); | |||
| static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {} | ||||
| #endif | ||||
| 
 | ||||
| int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) \ | ||||
| int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) \ | ||||
| 				__attribute__ ((format (printf, 2, 3))); | ||||
| int snd_info_init(void); | ||||
| int snd_info_done(void); | ||||
| 
 | ||||
| int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len); | ||||
| char *snd_info_get_str(char *dest, char *src, int len); | ||||
| const char *snd_info_get_str(char *dest, const char *src, int len); | ||||
| struct snd_info_entry *snd_info_create_module_entry(struct module *module, | ||||
| 					       const char *name, | ||||
| 					       struct snd_info_entry *parent); | ||||
|  |  | |||
|  | @ -47,7 +47,11 @@ struct snd_dma_device { | |||
| #define SNDRV_DMA_TYPE_UNKNOWN		0	/* not defined */ | ||||
| #define SNDRV_DMA_TYPE_CONTINUOUS	1	/* continuous no-DMA memory */ | ||||
| #define SNDRV_DMA_TYPE_DEV		2	/* generic device continuous */ | ||||
| #ifdef CONFIG_SND_DMA_SGBUF | ||||
| #define SNDRV_DMA_TYPE_DEV_SG		3	/* generic device SG-buffer */ | ||||
| #else | ||||
| #define SNDRV_DMA_TYPE_DEV_SG	SNDRV_DMA_TYPE_DEV /* no SG-buf support */ | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * info for buffer allocation | ||||
|  | @ -60,6 +64,7 @@ struct snd_dma_buffer { | |||
| 	void *private_data;	/* private for allocator; don't touch */ | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_SND_DMA_SGBUF | ||||
| /*
 | ||||
|  * Scatter-Gather generic device pages | ||||
|  */ | ||||
|  | @ -107,6 +112,7 @@ static inline void *snd_sgbuf_get_ptr(struct snd_sg_buf *sgbuf, size_t offset) | |||
| { | ||||
| 	return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE; | ||||
| } | ||||
| #endif /* CONFIG_SND_DMA_SGBUF */ | ||||
| 
 | ||||
| /* allocate/release a buffer */ | ||||
| int snd_dma_alloc_pages(int type, struct device *dev, size_t size, | ||||
|  |  | |||
|  | @ -902,6 +902,7 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, | |||
| int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size); | ||||
| int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream); | ||||
| 
 | ||||
| #ifdef CONFIG_SND_DMA_SGBUF | ||||
| /*
 | ||||
|  * SG-buffer handling | ||||
|  */ | ||||
|  | @ -927,6 +928,28 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, | |||
| unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream, | ||||
| 					  unsigned int ofs, unsigned int size); | ||||
| 
 | ||||
| #else /* !SND_DMA_SGBUF */ | ||||
| /*
 | ||||
|  * fake using a continuous buffer | ||||
|  */ | ||||
| static inline dma_addr_t | ||||
| snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs) | ||||
| { | ||||
| 	return substream->runtime->dma_addr + ofs; | ||||
| } | ||||
| 
 | ||||
| static inline void * | ||||
| snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs) | ||||
| { | ||||
| 	return substream->runtime->dma_area + ofs; | ||||
| } | ||||
| 
 | ||||
| #define snd_pcm_sgbuf_ops_page	NULL | ||||
| 
 | ||||
| #define snd_pcm_sgbuf_get_chunk_size(subs, ofs, size)	(size) | ||||
| 
 | ||||
| #endif /* SND_DMA_SGBUF */ | ||||
| 
 | ||||
| /* handle mmap counter - PCM mmap callback should handle this counter properly */ | ||||
| static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area) | ||||
| { | ||||
|  | @ -965,4 +988,6 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) | |||
| 
 | ||||
| #define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime) | ||||
| 
 | ||||
| const char *snd_pcm_format_name(snd_pcm_format_t format); | ||||
| 
 | ||||
| #endif /* __SOUND_PCM_H */ | ||||
|  |  | |||
							
								
								
									
										83
									
								
								include/sound/sh_fsi.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								include/sound/sh_fsi.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | |||
| #ifndef __SOUND_FSI_H | ||||
| #define __SOUND_FSI_H | ||||
| 
 | ||||
| /*
 | ||||
|  * Fifo-attached Serial Interface (FSI) support for SH7724 | ||||
|  * | ||||
|  * Copyright (C) 2009 Renesas Solutions Corp. | ||||
|  * Kuninori Morimoto <morimoto.kuninori@renesas.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| /* flags format
 | ||||
| 
 | ||||
|  * 0xABCDEEFF | ||||
|  * | ||||
|  * A:  channel size for TDM (input) | ||||
|  * B:  channel size for TDM (ooutput) | ||||
|  * C:  inversion | ||||
|  * D:  mode | ||||
|  * E:  input format | ||||
|  * F:  output format | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/clk.h> | ||||
| #include <sound/soc.h> | ||||
| 
 | ||||
| /* TDM channel */ | ||||
| #define SH_FSI_SET_CH_I(x)	((x & 0xF) << 28) | ||||
| #define SH_FSI_SET_CH_O(x)	((x & 0xF) << 24) | ||||
| 
 | ||||
| #define SH_FSI_CH_IMASK		0xF0000000 | ||||
| #define SH_FSI_CH_OMASK		0x0F000000 | ||||
| #define SH_FSI_GET_CH_I(x)	((x & SH_FSI_CH_IMASK) >> 28) | ||||
| #define SH_FSI_GET_CH_O(x)	((x & SH_FSI_CH_OMASK) >> 24) | ||||
| 
 | ||||
| /* clock inversion */ | ||||
| #define SH_FSI_INVERSION_MASK	0x00F00000 | ||||
| #define SH_FSI_LRM_INV		(1 << 20) | ||||
| #define SH_FSI_BRM_INV		(1 << 21) | ||||
| #define SH_FSI_LRS_INV		(1 << 22) | ||||
| #define SH_FSI_BRS_INV		(1 << 23) | ||||
| 
 | ||||
| /* mode */ | ||||
| #define SH_FSI_MODE_MASK	0x000F0000 | ||||
| #define SH_FSI_IN_SLAVE_MODE	(1 << 16)  /* default master mode */ | ||||
| #define SH_FSI_OUT_SLAVE_MODE	(1 << 17)  /* default master mode */ | ||||
| 
 | ||||
| /* DI format */ | ||||
| #define SH_FSI_FMT_MASK		0x000000FF | ||||
| #define SH_FSI_IFMT(x)		(((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8) | ||||
| #define SH_FSI_OFMT(x)		(((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0) | ||||
| #define SH_FSI_GET_IFMT(x)	((x >> 8) & SH_FSI_FMT_MASK) | ||||
| #define SH_FSI_GET_OFMT(x)	((x >> 0) & SH_FSI_FMT_MASK) | ||||
| 
 | ||||
| #define SH_FSI_FMT_MONO		(1 << 0) | ||||
| #define SH_FSI_FMT_MONO_DELAY	(1 << 1) | ||||
| #define SH_FSI_FMT_PCM		(1 << 2) | ||||
| #define SH_FSI_FMT_I2S		(1 << 3) | ||||
| #define SH_FSI_FMT_TDM		(1 << 4) | ||||
| #define SH_FSI_FMT_TDM_DELAY	(1 << 5) | ||||
| 
 | ||||
| #define SH_FSI_IFMT_TDM_CH(x) \ | ||||
| 	(SH_FSI_IFMT(TDM)	| SH_FSI_SET_CH_I(x)) | ||||
| #define SH_FSI_IFMT_TDM_DELAY_CH(x) \ | ||||
| 	(SH_FSI_IFMT(TDM_DELAY)	| SH_FSI_SET_CH_I(x)) | ||||
| 
 | ||||
| #define SH_FSI_OFMT_TDM_CH(x) \ | ||||
| 	(SH_FSI_OFMT(TDM)	| SH_FSI_SET_CH_O(x)) | ||||
| #define SH_FSI_OFMT_TDM_DELAY_CH(x) \ | ||||
| 	(SH_FSI_OFMT(TDM_DELAY)	| SH_FSI_SET_CH_O(x)) | ||||
| 
 | ||||
| struct sh_fsi_platform_info { | ||||
| 	unsigned long porta_flags; | ||||
| 	unsigned long portb_flags; | ||||
| }; | ||||
| 
 | ||||
| extern struct snd_soc_dai fsi_soc_dai[2]; | ||||
| extern struct snd_soc_platform fsi_soc_platform; | ||||
| 
 | ||||
| #endif /* __SOUND_FSI_H */ | ||||
|  | @ -27,8 +27,8 @@ struct snd_pcm_substream; | |||
| #define SND_SOC_DAIFMT_I2S		0 /* I2S mode */ | ||||
| #define SND_SOC_DAIFMT_RIGHT_J		1 /* Right Justified mode */ | ||||
| #define SND_SOC_DAIFMT_LEFT_J		2 /* Left Justified mode */ | ||||
| #define SND_SOC_DAIFMT_DSP_A		3 /* L data msb after FRM LRC */ | ||||
| #define SND_SOC_DAIFMT_DSP_B		4 /* L data msb during FRM LRC */ | ||||
| #define SND_SOC_DAIFMT_DSP_A		3 /* L data MSB after FRM LRC */ | ||||
| #define SND_SOC_DAIFMT_DSP_B		4 /* L data MSB during FRM LRC */ | ||||
| #define SND_SOC_DAIFMT_AC97		5 /* AC97 */ | ||||
| 
 | ||||
| /* left and right justified also known as MSB and LSB respectively */ | ||||
|  | @ -38,7 +38,7 @@ struct snd_pcm_substream; | |||
| /*
 | ||||
|  * DAI Clock gating. | ||||
|  * | ||||
|  * DAI bit clocks can be be gated (disabled) when not the DAI is not | ||||
|  * DAI bit clocks can be be gated (disabled) when the DAI is not | ||||
|  * sending or receiving PCM data in a frame. This can be used to save power. | ||||
|  */ | ||||
| #define SND_SOC_DAIFMT_CONT		(0 << 4) /* continuous clock */ | ||||
|  | @ -51,21 +51,21 @@ struct snd_pcm_substream; | |||
|  * format. | ||||
|  */ | ||||
| #define SND_SOC_DAIFMT_NB_NF		(0 << 8) /* normal bit clock + frame */ | ||||
| #define SND_SOC_DAIFMT_NB_IF		(1 << 8) /* normal bclk + inv frm */ | ||||
| #define SND_SOC_DAIFMT_IB_NF		(2 << 8) /* invert bclk + nor frm */ | ||||
| #define SND_SOC_DAIFMT_IB_IF		(3 << 8) /* invert bclk + frm */ | ||||
| #define SND_SOC_DAIFMT_NB_IF		(1 << 8) /* normal BCLK + inv FRM */ | ||||
| #define SND_SOC_DAIFMT_IB_NF		(2 << 8) /* invert BCLK + nor FRM */ | ||||
| #define SND_SOC_DAIFMT_IB_IF		(3 << 8) /* invert BCLK + FRM */ | ||||
| 
 | ||||
| /*
 | ||||
|  * DAI hardware clock masters. | ||||
|  * | ||||
|  * This is wrt the codec, the inverse is true for the interface | ||||
|  * i.e. if the codec is clk and frm master then the interface is | ||||
|  * i.e. if the codec is clk and FRM master then the interface is | ||||
|  * clk and frame slave. | ||||
|  */ | ||||
| #define SND_SOC_DAIFMT_CBM_CFM		(0 << 12) /* codec clk & frm master */ | ||||
| #define SND_SOC_DAIFMT_CBS_CFM		(1 << 12) /* codec clk slave & frm master */ | ||||
| #define SND_SOC_DAIFMT_CBM_CFM		(0 << 12) /* codec clk & FRM master */ | ||||
| #define SND_SOC_DAIFMT_CBS_CFM		(1 << 12) /* codec clk slave & FRM master */ | ||||
| #define SND_SOC_DAIFMT_CBM_CFS		(2 << 12) /* codec clk master & frame slave */ | ||||
| #define SND_SOC_DAIFMT_CBS_CFS		(3 << 12) /* codec clk & frm slave */ | ||||
| #define SND_SOC_DAIFMT_CBS_CFS		(3 << 12) /* codec clk & FRM slave */ | ||||
| 
 | ||||
| #define SND_SOC_DAIFMT_FORMAT_MASK	0x000f | ||||
| #define SND_SOC_DAIFMT_CLOCK_MASK	0x00f0 | ||||
|  | @ -78,7 +78,13 @@ struct snd_pcm_substream; | |||
| #define SND_SOC_CLOCK_IN		0 | ||||
| #define SND_SOC_CLOCK_OUT		1 | ||||
| 
 | ||||
| #define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\ | ||||
| #define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 |\ | ||||
| 			       SNDRV_PCM_FMTBIT_S16_LE |\ | ||||
| 			       SNDRV_PCM_FMTBIT_S16_BE |\ | ||||
| 			       SNDRV_PCM_FMTBIT_S20_3LE |\ | ||||
| 			       SNDRV_PCM_FMTBIT_S20_3BE |\ | ||||
| 			       SNDRV_PCM_FMTBIT_S24_3LE |\ | ||||
| 			       SNDRV_PCM_FMTBIT_S24_3BE |\ | ||||
|                                SNDRV_PCM_FMTBIT_S32_LE |\ | ||||
|                                SNDRV_PCM_FMTBIT_S32_BE) | ||||
| 
 | ||||
|  | @ -106,7 +112,7 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, | |||
| int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); | ||||
| 
 | ||||
| int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, | ||||
| 	unsigned int mask, int slots); | ||||
| 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); | ||||
| 
 | ||||
| int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate); | ||||
| 
 | ||||
|  | @ -116,12 +122,12 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute); | |||
| /*
 | ||||
|  * Digital Audio Interface. | ||||
|  * | ||||
|  * Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97 | ||||
|  * operations an capabilities. Codec and platfom drivers will register a this | ||||
|  * Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97 | ||||
|  * operations and capabilities. Codec and platform drivers will register this | ||||
|  * structure for every DAI they have. | ||||
|  * | ||||
|  * This structure covers the clocking, formating and ALSA operations for each | ||||
|  * interface a | ||||
|  * interface. | ||||
|  */ | ||||
| struct snd_soc_dai_ops { | ||||
| 	/*
 | ||||
|  | @ -140,7 +146,8 @@ struct snd_soc_dai_ops { | |||
| 	 */ | ||||
| 	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt); | ||||
| 	int (*set_tdm_slot)(struct snd_soc_dai *dai, | ||||
| 		unsigned int mask, int slots); | ||||
| 		unsigned int tx_mask, unsigned int rx_mask, | ||||
| 		int slots, int slot_width); | ||||
| 	int (*set_tristate)(struct snd_soc_dai *dai, int tristate); | ||||
| 
 | ||||
| 	/*
 | ||||
|  | @ -179,6 +186,7 @@ struct snd_soc_dai { | |||
| 	int ac97_control; | ||||
| 
 | ||||
| 	struct device *dev; | ||||
| 	void *ac97_pdata;	/* platform_data for the ac97 codec */ | ||||
| 
 | ||||
| 	/* DAI callbacks */ | ||||
| 	int (*probe)(struct platform_device *pdev, | ||||
|  |  | |||
|  | @ -137,6 +137,12 @@ | |||
| 	.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD} | ||||
| 
 | ||||
| /* stream domain */ | ||||
| #define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \ | ||||
| {	.id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ | ||||
| 	.reg = wreg, .shift = wshift, .invert = winvert } | ||||
| #define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \ | ||||
| {	.id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ | ||||
| 	.reg = wreg, .shift = wshift, .invert = winvert } | ||||
| #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \ | ||||
| {	.id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ | ||||
| 	.shift = wshift, .invert = winvert} | ||||
|  | @ -279,9 +285,11 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec, | |||
| /* dapm events */ | ||||
| int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream, | ||||
| 	int event); | ||||
| void snd_soc_dapm_shutdown(struct snd_soc_device *socdev); | ||||
| 
 | ||||
| /* dapm sys fs - used by the core */ | ||||
| int snd_soc_dapm_sys_add(struct device *dev); | ||||
| void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec); | ||||
| 
 | ||||
| /* dapm audio pin control and status */ | ||||
| int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin); | ||||
|  | @ -311,6 +319,8 @@ enum snd_soc_dapm_type { | |||
| 	snd_soc_dapm_pre,			/* machine specific pre widget - exec first */ | ||||
| 	snd_soc_dapm_post,			/* machine specific post widget - exec last */ | ||||
| 	snd_soc_dapm_supply,		/* power/clock supply */ | ||||
| 	snd_soc_dapm_aif_in,		/* audio interface input */ | ||||
| 	snd_soc_dapm_aif_out,		/* audio interface output */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
|  | @ -135,6 +135,28 @@ | |||
| 	.info = snd_soc_info_volsw, \ | ||||
| 	.get = xhandler_get, .put = xhandler_put, \ | ||||
| 	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } | ||||
| #define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\ | ||||
| 	 xhandler_get, xhandler_put, tlv_array) \ | ||||
| {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ | ||||
| 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ | ||||
| 		 SNDRV_CTL_ELEM_ACCESS_READWRITE, \ | ||||
| 	.tlv.p = (tlv_array), \ | ||||
| 	.info = snd_soc_info_volsw, \ | ||||
| 	.get = xhandler_get, .put = xhandler_put, \ | ||||
| 	.private_value = (unsigned long)&(struct soc_mixer_control) \ | ||||
| 		{.reg = xreg, .shift = shift_left, .rshift = shift_right, \ | ||||
| 		.max = xmax, .invert = xinvert} } | ||||
| #define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\ | ||||
| 	 xhandler_get, xhandler_put, tlv_array) \ | ||||
| {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ | ||||
| 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ | ||||
| 		 SNDRV_CTL_ELEM_ACCESS_READWRITE, \ | ||||
| 	.tlv.p = (tlv_array), \ | ||||
| 	.info = snd_soc_info_volsw_2r, \ | ||||
| 	.get = xhandler_get, .put = xhandler_put, \ | ||||
| 	.private_value = (unsigned long)&(struct soc_mixer_control) \ | ||||
| 		{.reg = reg_left, .rreg = reg_right, .shift = xshift, \ | ||||
| 		.max = xmax, .invert = xinvert} } | ||||
| #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \ | ||||
| {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||||
| 	.info = snd_soc_info_bool_ext, \ | ||||
|  | @ -183,14 +205,28 @@ struct snd_soc_jack_gpio; | |||
| #endif | ||||
| 
 | ||||
| typedef int (*hw_write_t)(void *,const char* ,int); | ||||
| typedef int (*hw_read_t)(void *,char* ,int); | ||||
| 
 | ||||
| extern struct snd_ac97_bus_ops soc_ac97_ops; | ||||
| 
 | ||||
| enum snd_soc_control_type { | ||||
| 	SND_SOC_CUSTOM, | ||||
| 	SND_SOC_I2C, | ||||
| 	SND_SOC_SPI, | ||||
| }; | ||||
| 
 | ||||
| int snd_soc_register_platform(struct snd_soc_platform *platform); | ||||
| void snd_soc_unregister_platform(struct snd_soc_platform *platform); | ||||
| int snd_soc_register_codec(struct snd_soc_codec *codec); | ||||
| void snd_soc_unregister_codec(struct snd_soc_codec *codec); | ||||
| int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg); | ||||
| int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, | ||||
| 			       int addr_bits, int data_bits, | ||||
| 			       enum snd_soc_control_type control); | ||||
| 
 | ||||
| #ifdef CONFIG_PM | ||||
| int snd_soc_suspend_device(struct device *dev); | ||||
| int snd_soc_resume_device(struct device *dev); | ||||
| #endif | ||||
| 
 | ||||
| /* pcm <-> DAI connect */ | ||||
| void snd_soc_free_pcms(struct snd_soc_device *socdev); | ||||
|  | @ -216,9 +252,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, | |||
| 
 | ||||
| /* codec register bit access */ | ||||
| int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, | ||||
| 				unsigned short mask, unsigned short value); | ||||
| 				unsigned int mask, unsigned int value); | ||||
| int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, | ||||
| 				unsigned short mask, unsigned short value); | ||||
| 				unsigned int mask, unsigned int value); | ||||
| 
 | ||||
| int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, | ||||
| 	struct snd_ac97_bus_ops *ops, int num); | ||||
|  | @ -356,8 +392,10 @@ struct snd_soc_codec { | |||
| 	int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); | ||||
| 	int (*display_register)(struct snd_soc_codec *, char *, | ||||
| 				size_t, unsigned int); | ||||
| 	int (*volatile_register)(unsigned int); | ||||
| 	int (*readable_register)(unsigned int); | ||||
| 	hw_write_t hw_write; | ||||
| 	hw_read_t hw_read; | ||||
| 	unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); | ||||
| 	void *reg_cache; | ||||
| 	short reg_cache_size; | ||||
| 	short reg_cache_step; | ||||
|  | @ -369,8 +407,6 @@ struct snd_soc_codec { | |||
| 	enum snd_soc_bias_level bias_level; | ||||
| 	enum snd_soc_bias_level suspend_bias_level; | ||||
| 	struct delayed_work delayed_work; | ||||
| 	struct list_head up_list; | ||||
| 	struct list_head down_list; | ||||
| 
 | ||||
| 	/* codec DAI's */ | ||||
| 	struct snd_soc_dai *dai; | ||||
|  | @ -379,6 +415,7 @@ struct snd_soc_codec { | |||
| #ifdef CONFIG_DEBUG_FS | ||||
| 	struct dentry *debugfs_reg; | ||||
| 	struct dentry *debugfs_pop_time; | ||||
| 	struct dentry *debugfs_dapm; | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -35,6 +35,8 @@ | |||
| #define SNDRV_CTL_TLVT_DB_SCALE	1       /* dB scale */ | ||||
| #define SNDRV_CTL_TLVT_DB_LINEAR 2	/* linear volume */ | ||||
| #define SNDRV_CTL_TLVT_DB_RANGE 3	/* dB range container */ | ||||
| #define SNDRV_CTL_TLVT_DB_MINMAX 4	/* dB scale with min/max */ | ||||
| #define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5	/* dB scale with min/max with mute */ | ||||
| 
 | ||||
| #define TLV_DB_SCALE_ITEM(min, step, mute)			\ | ||||
| 	SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int),	\ | ||||
|  | @ -42,6 +44,18 @@ | |||
| #define DECLARE_TLV_DB_SCALE(name, min, step, mute) \ | ||||
| 	unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) } | ||||
| 
 | ||||
| /* dB scale specified with min/max values instead of step */ | ||||
| #define TLV_DB_MINMAX_ITEM(min_dB, max_dB)			\ | ||||
| 	SNDRV_CTL_TLVT_DB_MINMAX, 2 * sizeof(unsigned int),	\ | ||||
| 	(min_dB), (max_dB) | ||||
| #define TLV_DB_MINMAX_MUTE_ITEM(min_dB, max_dB)			\ | ||||
| 	SNDRV_CTL_TLVT_DB_MINMAX_MUTE, 2 * sizeof(unsigned int),	\ | ||||
| 	(min_dB), (max_dB) | ||||
| #define DECLARE_TLV_DB_MINMAX(name, min_dB, max_dB) \ | ||||
| 	unsigned int name[] = { TLV_DB_MINMAX_ITEM(min_dB, max_dB) } | ||||
| #define DECLARE_TLV_DB_MINMAX_MUTE(name, min_dB, max_dB) \ | ||||
| 	unsigned int name[] = { TLV_DB_MINMAX_MUTE_ITEM(min_dB, max_dB) } | ||||
| 
 | ||||
| /* linear volume between min_dB and max_dB (.01dB unit) */ | ||||
| #define TLV_DB_LINEAR_ITEM(min_dB, max_dB)		    \ | ||||
| 	SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \ | ||||
|  |  | |||
							
								
								
									
										22
									
								
								include/sound/uda1380.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								include/sound/uda1380.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| /*
 | ||||
|  * UDA1380 ALSA SoC Codec driver | ||||
|  * | ||||
|  * Copyright 2009 Philipp Zabel | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __UDA1380_H | ||||
| #define __UDA1380_H | ||||
| 
 | ||||
| struct uda1380_platform_data { | ||||
| 	int gpio_power; | ||||
| 	int gpio_reset; | ||||
| 	int dac_clk; | ||||
| #define UDA1380_DAC_CLK_SYSCLK 0 | ||||
| #define UDA1380_DAC_CLK_WSPLL  1 | ||||
| }; | ||||
| 
 | ||||
| #endif /* __UDA1380_H */ | ||||
|  | @ -1,3 +1,3 @@ | |||
| /* include/version.h */ | ||||
| #define CONFIG_SND_VERSION "1.0.20" | ||||
| #define CONFIG_SND_VERSION "1.0.21" | ||||
| #define CONFIG_SND_DATE "" | ||||
|  |  | |||
							
								
								
									
										44
									
								
								include/sound/wm8993.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								include/sound/wm8993.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| /*
 | ||||
|  * linux/sound/wm8993.h -- Platform data for WM8993 | ||||
|  * | ||||
|  * Copyright 2009 Wolfson Microelectronics. PLC. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __LINUX_SND_WM8993_H | ||||
| #define __LINUX_SND_WM8993_H | ||||
| 
 | ||||
| /* Note that EQ1 only contains the enable/disable bit so will be
 | ||||
|    ignored but is included for simplicity. | ||||
|  */ | ||||
| struct wm8993_retune_mobile_setting { | ||||
| 	const char *name; | ||||
| 	unsigned int rate; | ||||
| 	u16 config[24]; | ||||
| }; | ||||
| 
 | ||||
| struct wm8993_platform_data { | ||||
| 	struct wm8993_retune_mobile_setting *retune_configs; | ||||
| 	int num_retune_configs; | ||||
| 
 | ||||
| 	/* LINEOUT can be differential or single ended */ | ||||
| 	unsigned int lineout1_diff:1; | ||||
| 	unsigned int lineout2_diff:1; | ||||
| 
 | ||||
| 	/* Common mode feedback */ | ||||
| 	unsigned int lineout1fb:1; | ||||
| 	unsigned int lineout2fb:1; | ||||
| 
 | ||||
| 	/* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ | ||||
| 	unsigned int micbias1_lvl:1; | ||||
| 	unsigned int micbias2_lvl:1; | ||||
| 
 | ||||
| 	/* Jack detect threashold levels, see datasheet for values */ | ||||
| 	unsigned int jd_scthr:2; | ||||
| 	unsigned int jd_thr:2; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | @ -331,6 +331,7 @@ struct snd_ymfpci { | |||
| 	struct snd_ac97 *ac97; | ||||
| 	struct snd_rawmidi *rawmidi; | ||||
| 	struct snd_timer *timer; | ||||
| 	unsigned int timer_ticks; | ||||
| 
 | ||||
| 	struct pci_dev *pci; | ||||
| 	struct snd_card *card; | ||||
|  |  | |||
|  | @ -32,6 +32,34 @@ config SOUND_OSS_CORE | |||
| 	bool | ||||
| 	default n | ||||
| 
 | ||||
| config SOUND_OSS_CORE_PRECLAIM | ||||
| 	bool "Preclaim OSS device numbers" | ||||
| 	depends on SOUND_OSS_CORE | ||||
| 	default y | ||||
| 	help | ||||
| 	  With this option enabled, the kernel will claim all OSS device | ||||
| 	  numbers if any OSS support (native or emulation) is enabled | ||||
| 	  whether the respective module is loaded or not and try to load the | ||||
| 	  appropriate module using sound-slot/service-* and char-major-* | ||||
| 	  module aliases when one of the device numbers is opened.  With | ||||
| 	  this option disabled, kernel will only claim actually in-use | ||||
| 	  device numbers and opening a missing device will generate only the | ||||
| 	  standard char-major-* aliases. | ||||
| 
 | ||||
| 	  The only visible difference is use of additional module aliases | ||||
| 	  and whether OSS sound devices appear multiple times in | ||||
| 	  /proc/devices.  sound-slot/service-* module aliases are scheduled | ||||
| 	  to be removed (ie. PRECLAIM won't be available) and this option is | ||||
| 	  to make the transition easier.  This option can be overridden | ||||
| 	  during boot using the kernel parameter soundcore.preclaim_oss. | ||||
| 
 | ||||
| 	  Disabling this allows alternative OSS implementations. | ||||
| 
 | ||||
| 	  Please read Documentation/feature-removal-schedule.txt for | ||||
| 	  details. | ||||
| 
 | ||||
| 	  If unusre, say Y. | ||||
| 
 | ||||
| source "sound/oss/dmasound/Kconfig" | ||||
| 
 | ||||
| if !M68K | ||||
|  |  | |||
|  | @ -170,6 +170,13 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev) | |||
| 	struct snd_ac97_bus *ac97_bus; | ||||
| 	struct snd_ac97_template ac97_template; | ||||
| 	int ret; | ||||
| 	pxa2xx_audio_ops_t *pdata = dev->dev.platform_data; | ||||
| 
 | ||||
| 	if (dev->id >= 0) { | ||||
| 		dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n"); | ||||
| 		ret = -ENXIO; | ||||
| 		goto err_dev; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, | ||||
| 			      THIS_MODULE, 0, &card); | ||||
|  | @ -200,6 +207,8 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev) | |||
| 	snprintf(card->longname, sizeof(card->longname), | ||||
| 		 "%s (%s)", dev->dev.driver->name, card->mixername); | ||||
| 
 | ||||
| 	if (pdata && pdata->codec_pdata[0]) | ||||
| 		snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]); | ||||
| 	snd_card_set_dev(card, &dev->dev); | ||||
| 	ret = snd_card_register(card); | ||||
| 	if (ret == 0) { | ||||
|  | @ -212,6 +221,7 @@ err_remove: | |||
| err: | ||||
| 	if (card) | ||||
| 		snd_card_free(card); | ||||
| err_dev: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -136,6 +136,9 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) | |||
| { | ||||
| 	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; | ||||
| 
 | ||||
| 	if (!prtd || !prtd->params) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	DCSR(prtd->dma_ch) &= ~DCSR_RUN; | ||||
| 	DCSR(prtd->dma_ch) = 0; | ||||
| 	DCMD(prtd->dma_ch) = 0; | ||||
|  |  | |||
|  | @ -206,4 +206,8 @@ config SND_PCM_XRUN_DEBUG | |||
| config SND_VMASTER | ||||
| 	bool | ||||
| 
 | ||||
| config SND_DMA_SGBUF | ||||
| 	def_bool y | ||||
| 	depends on X86 | ||||
| 
 | ||||
| source "sound/core/seq/Kconfig" | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ | |||
| 		pcm_memory.o | ||||
| 
 | ||||
| snd-page-alloc-y := memalloc.o | ||||
| snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o | ||||
| snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o | ||||
| 
 | ||||
| snd-rawmidi-objs  := rawmidi.o | ||||
| snd-timer-objs    := timer.o | ||||
|  |  | |||
|  | @ -414,7 +414,7 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) | |||
| EXPORT_SYMBOL(snd_ctl_remove_id); | ||||
| 
 | ||||
| /**
 | ||||
|  * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it | ||||
|  * snd_ctl_remove_user_ctl - remove and release the unlocked user control | ||||
|  * @file: active control handle | ||||
|  * @id: the control id to remove | ||||
|  * | ||||
|  | @ -423,8 +423,8 @@ EXPORT_SYMBOL(snd_ctl_remove_id); | |||
|  *  | ||||
|  * Returns 0 if successful, or a negative error code on failure. | ||||
|  */ | ||||
| static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file, | ||||
| 				      struct snd_ctl_elem_id *id) | ||||
| static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file, | ||||
| 				   struct snd_ctl_elem_id *id) | ||||
| { | ||||
| 	struct snd_card *card = file->card; | ||||
| 	struct snd_kcontrol *kctl; | ||||
|  | @ -433,15 +433,23 @@ static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file, | |||
| 	down_write(&card->controls_rwsem); | ||||
| 	kctl = snd_ctl_find_id(card, id); | ||||
| 	if (kctl == NULL) { | ||||
| 		up_write(&card->controls_rwsem); | ||||
| 		return -ENOENT; | ||||
| 		ret = -ENOENT; | ||||
| 		goto error; | ||||
| 	} | ||||
| 	if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) { | ||||
| 		ret = -EINVAL; | ||||
| 		goto error; | ||||
| 	} | ||||
| 	for (idx = 0; idx < kctl->count; idx++) | ||||
| 		if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) { | ||||
| 			up_write(&card->controls_rwsem); | ||||
| 			return -EBUSY; | ||||
| 			ret = -EBUSY; | ||||
| 			goto error; | ||||
| 		} | ||||
| 	ret = snd_ctl_remove(card, kctl); | ||||
| 	if (ret < 0) | ||||
| 		goto error; | ||||
| 	card->user_ctl_count--; | ||||
| error: | ||||
| 	up_write(&card->controls_rwsem); | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -951,7 +959,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, | |||
| 	 | ||||
| 	if (card->user_ctl_count >= MAX_USER_CONTROLS) | ||||
| 		return -ENOMEM; | ||||
| 	if (info->count > 1024) | ||||
| 	if (info->count < 1) | ||||
| 		return -EINVAL; | ||||
| 	access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : | ||||
| 		(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| | ||||
|  | @ -1052,18 +1060,10 @@ static int snd_ctl_elem_remove(struct snd_ctl_file *file, | |||
| 			       struct snd_ctl_elem_id __user *_id) | ||||
| { | ||||
| 	struct snd_ctl_elem_id id; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (copy_from_user(&id, _id, sizeof(id))) | ||||
| 		return -EFAULT; | ||||
| 	err = snd_ctl_remove_unlocked_id(file, &id); | ||||
| 	if (! err) { | ||||
| 		struct snd_card *card = file->card; | ||||
| 		down_write(&card->controls_rwsem); | ||||
| 		card->user_ctl_count--; | ||||
| 		up_write(&card->controls_rwsem); | ||||
| 	} | ||||
| 	return err; | ||||
| 	return snd_ctl_remove_user_ctl(file, &id); | ||||
| } | ||||
| 
 | ||||
| static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr) | ||||
|  |  | |||
|  | @ -88,12 +88,10 @@ static int resize_info_buffer(struct snd_info_buffer *buffer, | |||
| 	char *nbuf; | ||||
| 
 | ||||
| 	nsize = PAGE_ALIGN(nsize); | ||||
| 	nbuf = kmalloc(nsize, GFP_KERNEL); | ||||
| 	nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL); | ||||
| 	if (! nbuf) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	memcpy(nbuf, buffer->buffer, buffer->len); | ||||
| 	kfree(buffer->buffer); | ||||
| 	buffer->buffer = nbuf; | ||||
| 	buffer->len = nsize; | ||||
| 	return 0; | ||||
|  | @ -108,7 +106,7 @@ static int resize_info_buffer(struct snd_info_buffer *buffer, | |||
|  * | ||||
|  * Returns the size of output string. | ||||
|  */ | ||||
| int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...) | ||||
| int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) | ||||
| { | ||||
| 	va_list args; | ||||
| 	int len, res; | ||||
|  | @ -727,7 +725,7 @@ EXPORT_SYMBOL(snd_info_get_line); | |||
|  * Returns the updated pointer of the original string so that | ||||
|  * it can be used for the next call. | ||||
|  */ | ||||
| char *snd_info_get_str(char *dest, char *src, int len) | ||||
| const char *snd_info_get_str(char *dest, const char *src, int len) | ||||
| { | ||||
| 	int c; | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,6 +31,14 @@ | |||
| #include <sound/control.h> | ||||
| #include <sound/info.h> | ||||
| 
 | ||||
| /* monitor files for graceful shutdown (hotplug) */ | ||||
| struct snd_monitor_file { | ||||
| 	struct file *file; | ||||
| 	const struct file_operations *disconnected_f_op; | ||||
| 	struct list_head shutdown_list;	/* still need to shutdown */ | ||||
| 	struct list_head list;	/* link of monitor files */ | ||||
| }; | ||||
| 
 | ||||
| static DEFINE_SPINLOCK(shutdown_lock); | ||||
| static LIST_HEAD(shutdown_files); | ||||
| 
 | ||||
|  |  | |||
|  | @ -199,6 +199,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, | |||
| 	case SNDRV_DMA_TYPE_DEV: | ||||
| 		dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); | ||||
| 		break; | ||||
| #endif | ||||
| #ifdef CONFIG_SND_DMA_SGBUF | ||||
| 	case SNDRV_DMA_TYPE_DEV_SG: | ||||
| 		snd_malloc_sgbuf_pages(device, size, dmab, NULL); | ||||
| 		break; | ||||
|  | @ -269,6 +271,8 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) | |||
| 	case SNDRV_DMA_TYPE_DEV: | ||||
| 		snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); | ||||
| 		break; | ||||
| #endif | ||||
| #ifdef CONFIG_SND_DMA_SGBUF | ||||
| 	case SNDRV_DMA_TYPE_DEV_SG: | ||||
| 		snd_free_sgbuf_pages(dmab); | ||||
| 		break; | ||||
|  |  | |||
|  | @ -24,6 +24,20 @@ | |||
| #include <linux/ioport.h> | ||||
| #include <sound/core.h> | ||||
| 
 | ||||
| #ifdef CONFIG_SND_DEBUG | ||||
| 
 | ||||
| #ifdef CONFIG_SND_DEBUG_VERBOSE | ||||
| #define DEFAULT_DEBUG_LEVEL	2 | ||||
| #else | ||||
| #define DEFAULT_DEBUG_LEVEL	1 | ||||
| #endif | ||||
| 
 | ||||
| static int debug = DEFAULT_DEBUG_LEVEL; | ||||
| module_param(debug, int, 0644); | ||||
| MODULE_PARM_DESC(debug, "Debug level (0 = disable)"); | ||||
| 
 | ||||
| #endif /* CONFIG_SND_DEBUG */ | ||||
| 
 | ||||
| void release_and_free_resource(struct resource *res) | ||||
| { | ||||
| 	if (res) { | ||||
|  | @ -35,46 +49,53 @@ void release_and_free_resource(struct resource *res) | |||
| EXPORT_SYMBOL(release_and_free_resource); | ||||
| 
 | ||||
| #ifdef CONFIG_SND_VERBOSE_PRINTK | ||||
| void snd_verbose_printk(const char *file, int line, const char *format, ...) | ||||
| /* strip the leading path if the given path is absolute */ | ||||
| static const char *sanity_file_name(const char *path) | ||||
| { | ||||
| 	va_list args; | ||||
| 	 | ||||
| 	if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') { | ||||
| 		char tmp[] = "<0>"; | ||||
| 		tmp[1] = format[1]; | ||||
| 		printk("%sALSA %s:%d: ", tmp, file, line); | ||||
| 		format += 3; | ||||
| 	} else { | ||||
| 		printk("ALSA %s:%d: ", file, line); | ||||
| 	} | ||||
| 	va_start(args, format); | ||||
| 	vprintk(format, args); | ||||
| 	va_end(args); | ||||
| 	if (*path == '/') | ||||
| 		return strrchr(path, '/') + 1; | ||||
| 	else | ||||
| 		return path; | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL(snd_verbose_printk); | ||||
| /* print file and line with a certain printk prefix */ | ||||
| static int print_snd_pfx(unsigned int level, const char *path, int line, | ||||
| 			 const char *format) | ||||
| { | ||||
| 	const char *file = sanity_file_name(path); | ||||
| 	char tmp[] = "<0>"; | ||||
| 	const char *pfx = level ? KERN_DEBUG : KERN_DEFAULT; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (format[0] == '<' && format[2] == '>') { | ||||
| 		tmp[1] = format[1]; | ||||
| 		pfx = tmp; | ||||
| 		ret = 1; | ||||
| 	} | ||||
| 	printk("%sALSA %s:%d: ", pfx, file, line); | ||||
| 	return ret; | ||||
| } | ||||
| #else | ||||
| #define print_snd_pfx(level, path, line, format)	0 | ||||
| #endif | ||||
| 
 | ||||
| #if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) | ||||
| void snd_verbose_printd(const char *file, int line, const char *format, ...) | ||||
| #if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK) | ||||
| void __snd_printk(unsigned int level, const char *path, int line, | ||||
| 		  const char *format, ...) | ||||
| { | ||||
| 	va_list args; | ||||
| 	 | ||||
| 	if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') { | ||||
| 		char tmp[] = "<0>"; | ||||
| 		tmp[1] = format[1]; | ||||
| 		printk("%sALSA %s:%d: ", tmp, file, line); | ||||
| 		format += 3; | ||||
| 	} else { | ||||
| 		printk(KERN_DEBUG "ALSA %s:%d: ", file, line); | ||||
| 	} | ||||
| #ifdef CONFIG_SND_DEBUG	 | ||||
| 	if (debug < level) | ||||
| 		return; | ||||
| #endif | ||||
| 	va_start(args, format); | ||||
| 	if (print_snd_pfx(level, path, line, format)) | ||||
| 		format += 3; /* skip the printk level-prefix */ | ||||
| 	vprintk(format, args); | ||||
| 	va_end(args); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL(snd_verbose_printd); | ||||
| EXPORT_SYMBOL_GPL(__snd_printk); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_PCI | ||||
|  |  | |||
|  | @ -1154,7 +1154,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, | |||
| 				     struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	struct snd_mixer_oss *mixer = entry->private_data; | ||||
| 	char line[128], str[32], idxstr[16], *cptr; | ||||
| 	char line[128], str[32], idxstr[16]; | ||||
| 	const char *cptr; | ||||
| 	int ch, idx; | ||||
| 	struct snd_mixer_oss_assign_table *tbl; | ||||
| 	struct slot *slot; | ||||
|  |  | |||
|  | @ -1043,10 +1043,15 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) | |||
| 	runtime->oss.channels = params_channels(params); | ||||
| 	runtime->oss.rate = params_rate(params); | ||||
| 
 | ||||
| 	runtime->oss.params = 0; | ||||
| 	runtime->oss.prepare = 1; | ||||
| 	vfree(runtime->oss.buffer); | ||||
| 	runtime->oss.buffer = vmalloc(runtime->oss.period_bytes); | ||||
| 	if (!runtime->oss.buffer) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto failure; | ||||
| 	} | ||||
| 
 | ||||
| 	runtime->oss.params = 0; | ||||
| 	runtime->oss.prepare = 1; | ||||
| 	runtime->oss.buffer_used = 0; | ||||
| 	if (runtime->dma_area) | ||||
| 		snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes)); | ||||
|  | @ -2836,7 +2841,8 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, | |||
| 				   struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	struct snd_pcm_str *pstr = entry->private_data; | ||||
| 	char line[128], str[32], task_name[32], *ptr; | ||||
| 	char line[128], str[32], task_name[32]; | ||||
| 	const char *ptr; | ||||
| 	int idx1; | ||||
| 	struct snd_pcm_oss_setup *setup, *setup1, template; | ||||
| 
 | ||||
|  |  | |||
|  | @ -162,18 +162,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card, | |||
| 	return -ENOIOCTLCMD; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_SND_VERBOSE_PROCFS | ||||
| 
 | ||||
| #define STATE(v) [SNDRV_PCM_STATE_##v] = #v | ||||
| #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v | ||||
| #define READY(v) [SNDRV_PCM_READY_##v] = #v | ||||
| #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v | ||||
| #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v | ||||
| #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v | ||||
| #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v | ||||
| #define START(v) [SNDRV_PCM_START_##v] = #v | ||||
| #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v | ||||
| #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v  | ||||
| 
 | ||||
| static char *snd_pcm_format_names[] = { | ||||
| 	FORMAT(S8), | ||||
|  | @ -216,10 +205,23 @@ static char *snd_pcm_format_names[] = { | |||
| 	FORMAT(U18_3BE), | ||||
| }; | ||||
| 
 | ||||
| static const char *snd_pcm_format_name(snd_pcm_format_t format) | ||||
| const char *snd_pcm_format_name(snd_pcm_format_t format) | ||||
| { | ||||
| 	return snd_pcm_format_names[format]; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_pcm_format_name); | ||||
| 
 | ||||
| #ifdef CONFIG_SND_VERBOSE_PROCFS | ||||
| 
 | ||||
| #define STATE(v) [SNDRV_PCM_STATE_##v] = #v | ||||
| #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v | ||||
| #define READY(v) [SNDRV_PCM_READY_##v] = #v | ||||
| #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v | ||||
| #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v | ||||
| #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v | ||||
| #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v | ||||
| #define START(v) [SNDRV_PCM_START_##v] = #v | ||||
| #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v  | ||||
| 
 | ||||
| static char *snd_pcm_stream_names[] = { | ||||
| 	STREAM(PLAYBACK), | ||||
|  |  | |||
|  | @ -197,12 +197,16 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, | |||
| 		avail = snd_pcm_capture_avail(runtime); | ||||
| 	if (avail > runtime->avail_max) | ||||
| 		runtime->avail_max = avail; | ||||
| 	if (avail >= runtime->stop_threshold) { | ||||
| 		if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING) | ||||
| 	if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { | ||||
| 		if (avail >= runtime->buffer_size) { | ||||
| 			snd_pcm_drain_done(substream); | ||||
| 		else | ||||
| 			return -EPIPE; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (avail >= runtime->stop_threshold) { | ||||
| 			xrun(substream); | ||||
| 		return -EPIPE; | ||||
| 			return -EPIPE; | ||||
| 		} | ||||
| 	} | ||||
| 	if (avail >= runtime->control->avail_min) | ||||
| 		wake_up(&runtime->sleep); | ||||
|  |  | |||
|  | @ -304,6 +304,7 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, | |||
| 
 | ||||
| EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); | ||||
| 
 | ||||
| #ifdef CONFIG_SND_DMA_SGBUF | ||||
| /**
 | ||||
|  * snd_pcm_sgbuf_ops_page - get the page struct at the given offset | ||||
|  * @substream: the pcm substream instance | ||||
|  | @ -349,6 +350,7 @@ unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream, | |||
| 	return size; | ||||
| } | ||||
| EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size); | ||||
| #endif /* CONFIG_SND_DMA_SGBUF */ | ||||
| 
 | ||||
| /**
 | ||||
|  * snd_pcm_lib_malloc_pages - allocate the DMA buffer | ||||
|  |  | |||
|  | @ -1343,8 +1343,6 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, | |||
| 
 | ||||
| static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state) | ||||
| { | ||||
| 	if (substream->f_flags & O_NONBLOCK) | ||||
| 		return -EAGAIN; | ||||
| 	substream->runtime->trigger_master = substream; | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -1392,7 +1390,6 @@ static struct action_ops snd_pcm_action_drain_init = { | |||
| struct drain_rec { | ||||
| 	struct snd_pcm_substream *substream; | ||||
| 	wait_queue_t wait; | ||||
| 	snd_pcm_uframes_t stop_threshold; | ||||
| }; | ||||
| 
 | ||||
| static int snd_pcm_drop(struct snd_pcm_substream *substream); | ||||
|  | @ -1404,13 +1401,15 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream); | |||
|  * After this call, all streams are supposed to be either SETUP or DRAINING | ||||
|  * (capture only) state. | ||||
|  */ | ||||
| static int snd_pcm_drain(struct snd_pcm_substream *substream) | ||||
| static int snd_pcm_drain(struct snd_pcm_substream *substream, | ||||
| 			 struct file *file) | ||||
| { | ||||
| 	struct snd_card *card; | ||||
| 	struct snd_pcm_runtime *runtime; | ||||
| 	struct snd_pcm_substream *s; | ||||
| 	int result = 0; | ||||
| 	int i, num_drecs; | ||||
| 	int nonblock = 0; | ||||
| 	struct drain_rec *drec, drec_tmp, *d; | ||||
| 
 | ||||
| 	card = substream->pcm->card; | ||||
|  | @ -1428,6 +1427,15 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (file) { | ||||
| 		if (file->f_flags & O_NONBLOCK) | ||||
| 			nonblock = 1; | ||||
| 	} else if (substream->f_flags & O_NONBLOCK) | ||||
| 		nonblock = 1; | ||||
| 
 | ||||
| 	if (nonblock) | ||||
| 		goto lock; /* no need to allocate waitqueues */ | ||||
| 
 | ||||
| 	/* allocate temporary record for drain sync */ | ||||
| 	down_read(&snd_pcm_link_rwsem); | ||||
| 	if (snd_pcm_stream_linked(substream)) { | ||||
|  | @ -1449,16 +1457,11 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) | |||
| 			d->substream = s; | ||||
| 			init_waitqueue_entry(&d->wait, current); | ||||
| 			add_wait_queue(&runtime->sleep, &d->wait); | ||||
| 			/* stop_threshold fixup to avoid endless loop when
 | ||||
| 			 * stop_threshold > buffer_size | ||||
| 			 */ | ||||
| 			d->stop_threshold = runtime->stop_threshold; | ||||
| 			if (runtime->stop_threshold > runtime->buffer_size) | ||||
| 				runtime->stop_threshold = runtime->buffer_size; | ||||
| 		} | ||||
| 	} | ||||
| 	up_read(&snd_pcm_link_rwsem); | ||||
| 
 | ||||
|  lock: | ||||
| 	snd_pcm_stream_lock_irq(substream); | ||||
| 	/* resume pause */ | ||||
| 	if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED) | ||||
|  | @ -1466,9 +1469,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) | |||
| 
 | ||||
| 	/* pre-start/stop - all running streams are changed to DRAINING state */ | ||||
| 	result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0); | ||||
| 	if (result < 0) { | ||||
| 		snd_pcm_stream_unlock_irq(substream); | ||||
| 		goto _error; | ||||
| 	if (result < 0) | ||||
| 		goto unlock; | ||||
| 	/* in non-blocking, we don't wait in ioctl but let caller poll */ | ||||
| 	if (nonblock) { | ||||
| 		result = -EAGAIN; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	for (;;) { | ||||
|  | @ -1504,18 +1510,18 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  unlock: | ||||
| 	snd_pcm_stream_unlock_irq(substream); | ||||
| 
 | ||||
|  _error: | ||||
| 	for (i = 0; i < num_drecs; i++) { | ||||
| 		d = &drec[i]; | ||||
| 		runtime = d->substream->runtime; | ||||
| 		remove_wait_queue(&runtime->sleep, &d->wait); | ||||
| 		runtime->stop_threshold = d->stop_threshold; | ||||
| 	if (!nonblock) { | ||||
| 		for (i = 0; i < num_drecs; i++) { | ||||
| 			d = &drec[i]; | ||||
| 			runtime = d->substream->runtime; | ||||
| 			remove_wait_queue(&runtime->sleep, &d->wait); | ||||
| 		} | ||||
| 		if (drec != &drec_tmp) | ||||
| 			kfree(drec); | ||||
| 	} | ||||
| 
 | ||||
| 	if (drec != &drec_tmp) | ||||
| 		kfree(drec); | ||||
| 	snd_power_unlock(card); | ||||
| 
 | ||||
| 	return result; | ||||
|  | @ -2208,6 +2214,9 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst | |||
| 	case SNDRV_PCM_STATE_XRUN: | ||||
| 		ret = -EPIPE; | ||||
| 		goto __end; | ||||
| 	case SNDRV_PCM_STATE_SUSPENDED: | ||||
| 		ret = -ESTRPIPE; | ||||
| 		goto __end; | ||||
| 	default: | ||||
| 		ret = -EBADFD; | ||||
| 		goto __end; | ||||
|  | @ -2253,6 +2262,9 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr | |||
| 	case SNDRV_PCM_STATE_XRUN: | ||||
| 		ret = -EPIPE; | ||||
| 		goto __end; | ||||
| 	case SNDRV_PCM_STATE_SUSPENDED: | ||||
| 		ret = -ESTRPIPE; | ||||
| 		goto __end; | ||||
| 	default: | ||||
| 		ret = -EBADFD; | ||||
| 		goto __end; | ||||
|  | @ -2299,6 +2311,9 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs | |||
| 	case SNDRV_PCM_STATE_XRUN: | ||||
| 		ret = -EPIPE; | ||||
| 		goto __end; | ||||
| 	case SNDRV_PCM_STATE_SUSPENDED: | ||||
| 		ret = -ESTRPIPE; | ||||
| 		goto __end; | ||||
| 	default: | ||||
| 		ret = -EBADFD; | ||||
| 		goto __end; | ||||
|  | @ -2345,6 +2360,9 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst | |||
| 	case SNDRV_PCM_STATE_XRUN: | ||||
| 		ret = -EPIPE; | ||||
| 		goto __end; | ||||
| 	case SNDRV_PCM_STATE_SUSPENDED: | ||||
| 		ret = -ESTRPIPE; | ||||
| 		goto __end; | ||||
| 	default: | ||||
| 		ret = -EBADFD; | ||||
| 		goto __end; | ||||
|  | @ -2544,7 +2562,7 @@ static int snd_pcm_common_ioctl1(struct file *file, | |||
| 		return snd_pcm_hw_params_old_user(substream, arg); | ||||
| #endif | ||||
| 	case SNDRV_PCM_IOCTL_DRAIN: | ||||
| 		return snd_pcm_drain(substream); | ||||
| 		return snd_pcm_drain(substream, file); | ||||
| 	case SNDRV_PCM_IOCTL_DROP: | ||||
| 		return snd_pcm_drop(substream); | ||||
| 	case SNDRV_PCM_IOCTL_PAUSE: | ||||
|  |  | |||
|  | @ -274,7 +274,7 @@ static int open_substream(struct snd_rawmidi *rmidi, | |||
| 		return err; | ||||
| 	substream->opened = 1; | ||||
| 	if (substream->use_count++ == 0) | ||||
| 		substream->active_sensing = 1; | ||||
| 		substream->active_sensing = 0; | ||||
| 	if (mode & SNDRV_RAWMIDI_LFLG_APPEND) | ||||
| 		substream->append = 1; | ||||
| 	rmidi->streams[substream->stream].substream_opened++; | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ | |||
|  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | ||||
|  */ | ||||
| 
 | ||||
| #include <sound/asoundef.h> | ||||
| #include "seq_oss_midi.h" | ||||
| #include "seq_oss_readq.h" | ||||
| #include "seq_oss_timer.h" | ||||
|  | @ -476,19 +477,20 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev) | |||
| 		ev.source.port = dp->port; | ||||
| 		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) { | ||||
| 			ev.type = SNDRV_SEQ_EVENT_SENSING; | ||||
| 			snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */ | ||||
| 			snd_seq_oss_dispatch(dp, &ev, 0, 0); | ||||
| 		} | ||||
| 		for (c = 0; c < 16; c++) { | ||||
| 			ev.type = SNDRV_SEQ_EVENT_CONTROLLER; | ||||
| 			ev.data.control.channel = c; | ||||
| 			ev.data.control.param = 123; | ||||
| 			snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */ | ||||
| 			ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF; | ||||
| 			snd_seq_oss_dispatch(dp, &ev, 0, 0); | ||||
| 			if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { | ||||
| 				ev.data.control.param = 121; | ||||
| 				snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */ | ||||
| 				ev.data.control.param = | ||||
| 					MIDI_CTL_RESET_CONTROLLERS; | ||||
| 				snd_seq_oss_dispatch(dp, &ev, 0, 0); | ||||
| 				ev.type = SNDRV_SEQ_EVENT_PITCHBEND; | ||||
| 				ev.data.control.value = 0; | ||||
| 				snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */ | ||||
| 				snd_seq_oss_dispatch(dp, &ev, 0, 0); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -120,7 +120,8 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i | |||
| 		return -EINVAL; | ||||
| 	runtime = substream->runtime; | ||||
| 	if ((tmp = runtime->avail) < count) { | ||||
| 		snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp); | ||||
| 		if (printk_ratelimit()) | ||||
| 			snd_printk(KERN_ERR "MIDI output buffer overrun\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	if (snd_rawmidi_kernel_write(substream, buf, count) < count) | ||||
|  | @ -236,6 +237,7 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info | |||
| 	memset(¶ms, 0, sizeof(params)); | ||||
| 	params.avail_min = 1; | ||||
| 	params.buffer_size = output_buffer_size; | ||||
| 	params.no_active_sensing = 1; | ||||
| 	if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms)) < 0) { | ||||
| 		snd_rawmidi_kernel_release(&msynth->output_rfile); | ||||
| 		return err; | ||||
|  | @ -248,12 +250,9 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info | |||
| static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info) | ||||
| { | ||||
| 	struct seq_midisynth *msynth = private_data; | ||||
| 	unsigned char buf = 0xff; /* MIDI reset */ | ||||
| 
 | ||||
| 	if (snd_BUG_ON(!msynth->output_rfile.output)) | ||||
| 		return -EINVAL; | ||||
| 	/* sending single MIDI reset message to shut the device up */ | ||||
| 	snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1); | ||||
| 	snd_rawmidi_drain_output(msynth->output_rfile.output); | ||||
| 	return snd_rawmidi_kernel_release(&msynth->output_rfile); | ||||
| } | ||||
|  |  | |||
|  | @ -353,7 +353,8 @@ static void master_free(struct snd_kcontrol *kcontrol) | |||
|  * | ||||
|  * The optional argument @tlv can be used to specify the TLV information | ||||
|  * for dB scale of the master control.  It should be a single element | ||||
|  * with #SNDRV_CTL_TLVT_DB_SCALE type, and should be the max 0dB. | ||||
|  * with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or | ||||
|  * #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB. | ||||
|  */ | ||||
| struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, | ||||
| 						 const unsigned int *tlv) | ||||
|  | @ -384,7 +385,10 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, | |||
| 	kctl->private_free = master_free; | ||||
| 
 | ||||
| 	/* additional (constant) TLV read */ | ||||
| 	if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) { | ||||
| 	if (tlv && | ||||
| 	    (tlv[0] == SNDRV_CTL_TLVT_DB_SCALE || | ||||
| 	     tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX || | ||||
| 	     tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) { | ||||
| 		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; | ||||
| 		memcpy(master->tlv, tlv, sizeof(master->tlv)); | ||||
| 		kctl->tlv.p = master->tlv; | ||||
|  |  | |||
|  | @ -25,12 +25,15 @@ | |||
| #include <linux/slab.h> | ||||
| #include <linux/time.h> | ||||
| #include <linux/wait.h> | ||||
| #include <linux/hrtimer.h> | ||||
| #include <linux/math64.h> | ||||
| #include <linux/moduleparam.h> | ||||
| #include <sound/core.h> | ||||
| #include <sound/control.h> | ||||
| #include <sound/tlv.h> | ||||
| #include <sound/pcm.h> | ||||
| #include <sound/rawmidi.h> | ||||
| #include <sound/info.h> | ||||
| #include <sound/initval.h> | ||||
| 
 | ||||
| MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); | ||||
|  | @ -39,7 +42,7 @@ MODULE_LICENSE("GPL"); | |||
| MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}"); | ||||
| 
 | ||||
| #define MAX_PCM_DEVICES		4 | ||||
| #define MAX_PCM_SUBSTREAMS	16 | ||||
| #define MAX_PCM_SUBSTREAMS	128 | ||||
| #define MAX_MIDI_DEVICES	2 | ||||
| 
 | ||||
| #if 0 /* emu10k1 emulation */
 | ||||
|  | @ -148,6 +151,10 @@ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; | |||
| static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||||
| static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; | ||||
| //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
 | ||||
| #ifdef CONFIG_HIGH_RES_TIMERS | ||||
| static int hrtimer = 1; | ||||
| #endif | ||||
| static int fake_buffer = 1; | ||||
| 
 | ||||
| module_param_array(index, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(index, "Index value for dummy soundcard."); | ||||
|  | @ -161,6 +168,12 @@ module_param_array(pcm_substreams, int, NULL, 0444); | |||
| MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); | ||||
| //module_param_array(midi_devs, int, NULL, 0444);
 | ||||
| //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
 | ||||
| module_param(fake_buffer, bool, 0444); | ||||
| MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations."); | ||||
| #ifdef CONFIG_HIGH_RES_TIMERS | ||||
| module_param(hrtimer, bool, 0644); | ||||
| MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source."); | ||||
| #endif | ||||
| 
 | ||||
| static struct platform_device *devices[SNDRV_CARDS]; | ||||
| 
 | ||||
|  | @ -171,119 +184,324 @@ static struct platform_device *devices[SNDRV_CARDS]; | |||
| #define MIXER_ADDR_CD		4 | ||||
| #define MIXER_ADDR_LAST		4 | ||||
| 
 | ||||
| struct dummy_timer_ops { | ||||
| 	int (*create)(struct snd_pcm_substream *); | ||||
| 	void (*free)(struct snd_pcm_substream *); | ||||
| 	int (*prepare)(struct snd_pcm_substream *); | ||||
| 	int (*start)(struct snd_pcm_substream *); | ||||
| 	int (*stop)(struct snd_pcm_substream *); | ||||
| 	snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *); | ||||
| }; | ||||
| 
 | ||||
| struct snd_dummy { | ||||
| 	struct snd_card *card; | ||||
| 	struct snd_pcm *pcm; | ||||
| 	spinlock_t mixer_lock; | ||||
| 	int mixer_volume[MIXER_ADDR_LAST+1][2]; | ||||
| 	int capture_source[MIXER_ADDR_LAST+1][2]; | ||||
| 	const struct dummy_timer_ops *timer_ops; | ||||
| }; | ||||
| 
 | ||||
| struct snd_dummy_pcm { | ||||
| 	struct snd_dummy *dummy; | ||||
| /*
 | ||||
|  * system timer interface | ||||
|  */ | ||||
| 
 | ||||
| struct dummy_systimer_pcm { | ||||
| 	spinlock_t lock; | ||||
| 	struct timer_list timer; | ||||
| 	unsigned int pcm_buffer_size; | ||||
| 	unsigned int pcm_period_size; | ||||
| 	unsigned int pcm_bps;		/* bytes per second */ | ||||
| 	unsigned int pcm_hz;		/* HZ */ | ||||
| 	unsigned int pcm_irq_pos;	/* IRQ position */ | ||||
| 	unsigned int pcm_buf_pos;	/* position in buffer */ | ||||
| 	unsigned long base_time; | ||||
| 	unsigned int frac_pos;	/* fractional sample position (based HZ) */ | ||||
| 	unsigned int frac_period_rest; | ||||
| 	unsigned int frac_buffer_size;	/* buffer_size * HZ */ | ||||
| 	unsigned int frac_period_size;	/* period_size * HZ */ | ||||
| 	unsigned int rate; | ||||
| 	int elapsed; | ||||
| 	struct snd_pcm_substream *substream; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static inline void snd_card_dummy_pcm_timer_start(struct snd_dummy_pcm *dpcm) | ||||
| static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm) | ||||
| { | ||||
| 	dpcm->timer.expires = 1 + jiffies; | ||||
| 	dpcm->timer.expires = jiffies + | ||||
| 		(dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate; | ||||
| 	add_timer(&dpcm->timer); | ||||
| } | ||||
| 
 | ||||
| static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm) | ||||
| static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm) | ||||
| { | ||||
| 	del_timer(&dpcm->timer); | ||||
| 	unsigned long delta; | ||||
| 
 | ||||
| 	delta = jiffies - dpcm->base_time; | ||||
| 	if (!delta) | ||||
| 		return; | ||||
| 	dpcm->base_time += delta; | ||||
| 	delta *= dpcm->rate; | ||||
| 	dpcm->frac_pos += delta; | ||||
| 	while (dpcm->frac_pos >= dpcm->frac_buffer_size) | ||||
| 		dpcm->frac_pos -= dpcm->frac_buffer_size; | ||||
| 	while (dpcm->frac_period_rest <= delta) { | ||||
| 		dpcm->elapsed++; | ||||
| 		dpcm->frac_period_rest += dpcm->frac_period_size; | ||||
| 	} | ||||
| 	dpcm->frac_period_rest -= delta; | ||||
| } | ||||
| 
 | ||||
| static int snd_card_dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||||
| static int dummy_systimer_start(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_pcm_runtime *runtime = substream->runtime; | ||||
| 	struct snd_dummy_pcm *dpcm = runtime->private_data; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; | ||||
| 	spin_lock(&dpcm->lock); | ||||
| 	switch (cmd) { | ||||
| 	case SNDRV_PCM_TRIGGER_START: | ||||
| 	case SNDRV_PCM_TRIGGER_RESUME: | ||||
| 		snd_card_dummy_pcm_timer_start(dpcm); | ||||
| 		break; | ||||
| 	case SNDRV_PCM_TRIGGER_STOP: | ||||
| 	case SNDRV_PCM_TRIGGER_SUSPEND: | ||||
| 		snd_card_dummy_pcm_timer_stop(dpcm); | ||||
| 		break; | ||||
| 	default: | ||||
| 		err = -EINVAL; | ||||
| 		break; | ||||
| 	} | ||||
| 	dpcm->base_time = jiffies; | ||||
| 	dummy_systimer_rearm(dpcm); | ||||
| 	spin_unlock(&dpcm->lock); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream) | ||||
| static int dummy_systimer_stop(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; | ||||
| 	spin_lock(&dpcm->lock); | ||||
| 	del_timer(&dpcm->timer); | ||||
| 	spin_unlock(&dpcm->lock); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dummy_systimer_prepare(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_pcm_runtime *runtime = substream->runtime; | ||||
| 	struct snd_dummy_pcm *dpcm = runtime->private_data; | ||||
| 	int bps; | ||||
| 	struct dummy_systimer_pcm *dpcm = runtime->private_data; | ||||
| 
 | ||||
| 	bps = snd_pcm_format_width(runtime->format) * runtime->rate * | ||||
| 		runtime->channels / 8; | ||||
| 
 | ||||
| 	if (bps <= 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	dpcm->pcm_bps = bps; | ||||
| 	dpcm->pcm_hz = HZ; | ||||
| 	dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream); | ||||
| 	dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream); | ||||
| 	dpcm->pcm_irq_pos = 0; | ||||
| 	dpcm->pcm_buf_pos = 0; | ||||
| 
 | ||||
| 	snd_pcm_format_set_silence(runtime->format, runtime->dma_area, | ||||
| 			bytes_to_samples(runtime, runtime->dma_bytes)); | ||||
| 	dpcm->frac_pos = 0; | ||||
| 	dpcm->rate = runtime->rate; | ||||
| 	dpcm->frac_buffer_size = runtime->buffer_size * HZ; | ||||
| 	dpcm->frac_period_size = runtime->period_size * HZ; | ||||
| 	dpcm->frac_period_rest = dpcm->frac_period_size; | ||||
| 	dpcm->elapsed = 0; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void snd_card_dummy_pcm_timer_function(unsigned long data) | ||||
| static void dummy_systimer_callback(unsigned long data) | ||||
| { | ||||
| 	struct snd_dummy_pcm *dpcm = (struct snd_dummy_pcm *)data; | ||||
| 	struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data; | ||||
| 	unsigned long flags; | ||||
| 	int elapsed = 0; | ||||
| 	 | ||||
| 	spin_lock_irqsave(&dpcm->lock, flags); | ||||
| 	dpcm->timer.expires = 1 + jiffies; | ||||
| 	add_timer(&dpcm->timer); | ||||
| 	dpcm->pcm_irq_pos += dpcm->pcm_bps; | ||||
| 	dpcm->pcm_buf_pos += dpcm->pcm_bps; | ||||
| 	dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz; | ||||
| 	if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) { | ||||
| 		dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz; | ||||
| 		spin_unlock_irqrestore(&dpcm->lock, flags); | ||||
| 	dummy_systimer_update(dpcm); | ||||
| 	dummy_systimer_rearm(dpcm); | ||||
| 	elapsed = dpcm->elapsed; | ||||
| 	dpcm->elapsed = 0; | ||||
| 	spin_unlock_irqrestore(&dpcm->lock, flags); | ||||
| 	if (elapsed) | ||||
| 		snd_pcm_period_elapsed(dpcm->substream); | ||||
| 	} else | ||||
| 		spin_unlock_irqrestore(&dpcm->lock, flags); | ||||
| } | ||||
| 
 | ||||
| static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream) | ||||
| static snd_pcm_uframes_t | ||||
| dummy_systimer_pointer(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; | ||||
| 	snd_pcm_uframes_t pos; | ||||
| 
 | ||||
| 	spin_lock(&dpcm->lock); | ||||
| 	dummy_systimer_update(dpcm); | ||||
| 	pos = dpcm->frac_pos / HZ; | ||||
| 	spin_unlock(&dpcm->lock); | ||||
| 	return pos; | ||||
| } | ||||
| 
 | ||||
| static int dummy_systimer_create(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct dummy_systimer_pcm *dpcm; | ||||
| 
 | ||||
| 	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | ||||
| 	if (!dpcm) | ||||
| 		return -ENOMEM; | ||||
| 	substream->runtime->private_data = dpcm; | ||||
| 	init_timer(&dpcm->timer); | ||||
| 	dpcm->timer.data = (unsigned long) dpcm; | ||||
| 	dpcm->timer.function = dummy_systimer_callback; | ||||
| 	spin_lock_init(&dpcm->lock); | ||||
| 	dpcm->substream = substream; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dummy_systimer_free(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	kfree(substream->runtime->private_data); | ||||
| } | ||||
| 
 | ||||
| static struct dummy_timer_ops dummy_systimer_ops = { | ||||
| 	.create =	dummy_systimer_create, | ||||
| 	.free =		dummy_systimer_free, | ||||
| 	.prepare =	dummy_systimer_prepare, | ||||
| 	.start =	dummy_systimer_start, | ||||
| 	.stop =		dummy_systimer_stop, | ||||
| 	.pointer =	dummy_systimer_pointer, | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_HIGH_RES_TIMERS | ||||
| /*
 | ||||
|  * hrtimer interface | ||||
|  */ | ||||
| 
 | ||||
| struct dummy_hrtimer_pcm { | ||||
| 	ktime_t base_time; | ||||
| 	ktime_t period_time; | ||||
| 	atomic_t running; | ||||
| 	struct hrtimer timer; | ||||
| 	struct tasklet_struct tasklet; | ||||
| 	struct snd_pcm_substream *substream; | ||||
| }; | ||||
| 
 | ||||
| static void dummy_hrtimer_pcm_elapsed(unsigned long priv) | ||||
| { | ||||
| 	struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv; | ||||
| 	if (atomic_read(&dpcm->running)) | ||||
| 		snd_pcm_period_elapsed(dpcm->substream); | ||||
| } | ||||
| 
 | ||||
| static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer) | ||||
| { | ||||
| 	struct dummy_hrtimer_pcm *dpcm; | ||||
| 
 | ||||
| 	dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer); | ||||
| 	if (!atomic_read(&dpcm->running)) | ||||
| 		return HRTIMER_NORESTART; | ||||
| 	tasklet_schedule(&dpcm->tasklet); | ||||
| 	hrtimer_forward_now(timer, dpcm->period_time); | ||||
| 	return HRTIMER_RESTART; | ||||
| } | ||||
| 
 | ||||
| static int dummy_hrtimer_start(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; | ||||
| 
 | ||||
| 	dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer); | ||||
| 	hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL); | ||||
| 	atomic_set(&dpcm->running, 1); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dummy_hrtimer_stop(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; | ||||
| 
 | ||||
| 	atomic_set(&dpcm->running, 0); | ||||
| 	hrtimer_cancel(&dpcm->timer); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm) | ||||
| { | ||||
| 	tasklet_kill(&dpcm->tasklet); | ||||
| } | ||||
| 
 | ||||
| static snd_pcm_uframes_t | ||||
| dummy_hrtimer_pointer(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_pcm_runtime *runtime = substream->runtime; | ||||
| 	struct snd_dummy_pcm *dpcm = runtime->private_data; | ||||
| 	struct dummy_hrtimer_pcm *dpcm = runtime->private_data; | ||||
| 	u64 delta; | ||||
| 	u32 pos; | ||||
| 
 | ||||
| 	return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz); | ||||
| 	delta = ktime_us_delta(hrtimer_cb_get_time(&dpcm->timer), | ||||
| 			       dpcm->base_time); | ||||
| 	delta = div_u64(delta * runtime->rate + 999999, 1000000); | ||||
| 	div_u64_rem(delta, runtime->buffer_size, &pos); | ||||
| 	return pos; | ||||
| } | ||||
| 
 | ||||
| static struct snd_pcm_hardware snd_card_dummy_playback = | ||||
| static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||||
| 				 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), | ||||
| 	struct snd_pcm_runtime *runtime = substream->runtime; | ||||
| 	struct dummy_hrtimer_pcm *dpcm = runtime->private_data; | ||||
| 	unsigned int period, rate; | ||||
| 	long sec; | ||||
| 	unsigned long nsecs; | ||||
| 
 | ||||
| 	dummy_hrtimer_sync(dpcm); | ||||
| 	period = runtime->period_size; | ||||
| 	rate = runtime->rate; | ||||
| 	sec = period / rate; | ||||
| 	period %= rate; | ||||
| 	nsecs = div_u64((u64)period * 1000000000UL + rate - 1, rate); | ||||
| 	dpcm->period_time = ktime_set(sec, nsecs); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dummy_hrtimer_create(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct dummy_hrtimer_pcm *dpcm; | ||||
| 
 | ||||
| 	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | ||||
| 	if (!dpcm) | ||||
| 		return -ENOMEM; | ||||
| 	substream->runtime->private_data = dpcm; | ||||
| 	hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||||
| 	dpcm->timer.function = dummy_hrtimer_callback; | ||||
| 	dpcm->substream = substream; | ||||
| 	atomic_set(&dpcm->running, 0); | ||||
| 	tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed, | ||||
| 		     (unsigned long)dpcm); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dummy_hrtimer_free(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; | ||||
| 	dummy_hrtimer_sync(dpcm); | ||||
| 	kfree(dpcm); | ||||
| } | ||||
| 
 | ||||
| static struct dummy_timer_ops dummy_hrtimer_ops = { | ||||
| 	.create =	dummy_hrtimer_create, | ||||
| 	.free =		dummy_hrtimer_free, | ||||
| 	.prepare =	dummy_hrtimer_prepare, | ||||
| 	.start =	dummy_hrtimer_start, | ||||
| 	.stop =		dummy_hrtimer_stop, | ||||
| 	.pointer =	dummy_hrtimer_pointer, | ||||
| }; | ||||
| 
 | ||||
| #endif /* CONFIG_HIGH_RES_TIMERS */ | ||||
| 
 | ||||
| /*
 | ||||
|  * PCM interface | ||||
|  */ | ||||
| 
 | ||||
| static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||||
| { | ||||
| 	struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||||
| 
 | ||||
| 	switch (cmd) { | ||||
| 	case SNDRV_PCM_TRIGGER_START: | ||||
| 	case SNDRV_PCM_TRIGGER_RESUME: | ||||
| 		return dummy->timer_ops->start(substream); | ||||
| 	case SNDRV_PCM_TRIGGER_STOP: | ||||
| 	case SNDRV_PCM_TRIGGER_SUSPEND: | ||||
| 		return dummy->timer_ops->stop(substream); | ||||
| 	} | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| static int dummy_pcm_prepare(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||||
| 
 | ||||
| 	return dummy->timer_ops->prepare(substream); | ||||
| } | ||||
| 
 | ||||
| static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||||
| 
 | ||||
| 	return dummy->timer_ops->pointer(substream); | ||||
| } | ||||
| 
 | ||||
| static struct snd_pcm_hardware dummy_pcm_hardware = { | ||||
| 	.info =			(SNDRV_PCM_INFO_MMAP | | ||||
| 				 SNDRV_PCM_INFO_INTERLEAVED | | ||||
| 				 SNDRV_PCM_INFO_RESUME | | ||||
| 				 SNDRV_PCM_INFO_MMAP_VALID), | ||||
| 	.formats =		USE_FORMATS, | ||||
| 	.rates =		USE_RATE, | ||||
| 	.rate_min =		USE_RATE_MIN, | ||||
|  | @ -298,141 +516,152 @@ static struct snd_pcm_hardware snd_card_dummy_playback = | |||
| 	.fifo_size =		0, | ||||
| }; | ||||
| 
 | ||||
| static struct snd_pcm_hardware snd_card_dummy_capture = | ||||
| static int dummy_pcm_hw_params(struct snd_pcm_substream *substream, | ||||
| 			       struct snd_pcm_hw_params *hw_params) | ||||
| { | ||||
| 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||||
| 				 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), | ||||
| 	.formats =		USE_FORMATS, | ||||
| 	.rates =		USE_RATE, | ||||
| 	.rate_min =		USE_RATE_MIN, | ||||
| 	.rate_max =		USE_RATE_MAX, | ||||
| 	.channels_min =		USE_CHANNELS_MIN, | ||||
| 	.channels_max =		USE_CHANNELS_MAX, | ||||
| 	.buffer_bytes_max =	MAX_BUFFER_SIZE, | ||||
| 	.period_bytes_min =	64, | ||||
| 	.period_bytes_max =	MAX_PERIOD_SIZE, | ||||
| 	.periods_min =		USE_PERIODS_MIN, | ||||
| 	.periods_max =		USE_PERIODS_MAX, | ||||
| 	.fifo_size =		0, | ||||
| }; | ||||
| 
 | ||||
| static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime) | ||||
| { | ||||
| 	kfree(runtime->private_data); | ||||
| 	if (fake_buffer) { | ||||
| 		/* runtime->dma_bytes has to be set manually to allow mmap */ | ||||
| 		substream->runtime->dma_bytes = params_buffer_bytes(hw_params); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return snd_pcm_lib_malloc_pages(substream, | ||||
| 					params_buffer_bytes(hw_params)); | ||||
| } | ||||
| 
 | ||||
| static int snd_card_dummy_hw_params(struct snd_pcm_substream *substream, | ||||
| 				    struct snd_pcm_hw_params *hw_params) | ||||
| { | ||||
| 	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||||
| } | ||||
| 
 | ||||
| static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream) | ||||
| static int dummy_pcm_hw_free(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	if (fake_buffer) | ||||
| 		return 0; | ||||
| 	return snd_pcm_lib_free_pages(substream); | ||||
| } | ||||
| 
 | ||||
| static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_dummy_pcm *dpcm; | ||||
| 
 | ||||
| 	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | ||||
| 	if (! dpcm) | ||||
| 		return dpcm; | ||||
| 	init_timer(&dpcm->timer); | ||||
| 	dpcm->timer.data = (unsigned long) dpcm; | ||||
| 	dpcm->timer.function = snd_card_dummy_pcm_timer_function; | ||||
| 	spin_lock_init(&dpcm->lock); | ||||
| 	dpcm->substream = substream; | ||||
| 	return dpcm; | ||||
| } | ||||
| 
 | ||||
| static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream) | ||||
| static int dummy_pcm_open(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||||
| 	struct snd_pcm_runtime *runtime = substream->runtime; | ||||
| 	struct snd_dummy_pcm *dpcm; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if ((dpcm = new_pcm_stream(substream)) == NULL) | ||||
| 		return -ENOMEM; | ||||
| 	runtime->private_data = dpcm; | ||||
| 	/* makes the infrastructure responsible for freeing dpcm */ | ||||
| 	runtime->private_free = snd_card_dummy_runtime_free; | ||||
| 	runtime->hw = snd_card_dummy_playback; | ||||
| 	dummy->timer_ops = &dummy_systimer_ops; | ||||
| #ifdef CONFIG_HIGH_RES_TIMERS | ||||
| 	if (hrtimer) | ||||
| 		dummy->timer_ops = &dummy_hrtimer_ops; | ||||
| #endif | ||||
| 
 | ||||
| 	err = dummy->timer_ops->create(substream); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 
 | ||||
| 	runtime->hw = dummy_pcm_hardware; | ||||
| 	if (substream->pcm->device & 1) { | ||||
| 		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; | ||||
| 		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; | ||||
| 	} | ||||
| 	if (substream->pcm->device & 2) | ||||
| 		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); | ||||
| 	err = add_playback_constraints(runtime); | ||||
| 	if (err < 0) | ||||
| 		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | | ||||
| 				      SNDRV_PCM_INFO_MMAP_VALID); | ||||
| 
 | ||||
| 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||||
| 		err = add_playback_constraints(substream->runtime); | ||||
| 	else | ||||
| 		err = add_capture_constraints(substream->runtime); | ||||
| 	if (err < 0) { | ||||
| 		dummy->timer_ops->free(substream); | ||||
| 		return err; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_pcm_runtime *runtime = substream->runtime; | ||||
| 	struct snd_dummy_pcm *dpcm; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if ((dpcm = new_pcm_stream(substream)) == NULL) | ||||
| 		return -ENOMEM; | ||||
| 	runtime->private_data = dpcm; | ||||
| 	/* makes the infrastructure responsible for freeing dpcm */ | ||||
| 	runtime->private_free = snd_card_dummy_runtime_free; | ||||
| 	runtime->hw = snd_card_dummy_capture; | ||||
| 	if (substream->pcm->device == 1) { | ||||
| 		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; | ||||
| 		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; | ||||
| 	} | ||||
| 	if (substream->pcm->device & 2) | ||||
| 		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); | ||||
| 	err = add_capture_constraints(runtime); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream) | ||||
| static int dummy_pcm_close(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||||
| 	dummy->timer_ops->free(substream); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream) | ||||
| /*
 | ||||
|  * dummy buffer handling | ||||
|  */ | ||||
| 
 | ||||
| static void *dummy_page[2]; | ||||
| 
 | ||||
| static void free_fake_buffer(void) | ||||
| { | ||||
| 	if (fake_buffer) { | ||||
| 		int i; | ||||
| 		for (i = 0; i < 2; i++) | ||||
| 			if (dummy_page[i]) { | ||||
| 				free_page((unsigned long)dummy_page[i]); | ||||
| 				dummy_page[i] = NULL; | ||||
| 			} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int alloc_fake_buffer(void) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (!fake_buffer) | ||||
| 		return 0; | ||||
| 	for (i = 0; i < 2; i++) { | ||||
| 		dummy_page[i] = (void *)get_zeroed_page(GFP_KERNEL); | ||||
| 		if (!dummy_page[i]) { | ||||
| 			free_fake_buffer(); | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct snd_pcm_ops snd_card_dummy_playback_ops = { | ||||
| 	.open =			snd_card_dummy_playback_open, | ||||
| 	.close =		snd_card_dummy_playback_close, | ||||
| 	.ioctl =		snd_pcm_lib_ioctl, | ||||
| 	.hw_params =		snd_card_dummy_hw_params, | ||||
| 	.hw_free =		snd_card_dummy_hw_free, | ||||
| 	.prepare =		snd_card_dummy_pcm_prepare, | ||||
| 	.trigger =		snd_card_dummy_pcm_trigger, | ||||
| 	.pointer =		snd_card_dummy_pcm_pointer, | ||||
| static int dummy_pcm_copy(struct snd_pcm_substream *substream, | ||||
| 			  int channel, snd_pcm_uframes_t pos, | ||||
| 			  void __user *dst, snd_pcm_uframes_t count) | ||||
| { | ||||
| 	return 0; /* do nothing */ | ||||
| } | ||||
| 
 | ||||
| static int dummy_pcm_silence(struct snd_pcm_substream *substream, | ||||
| 			     int channel, snd_pcm_uframes_t pos, | ||||
| 			     snd_pcm_uframes_t count) | ||||
| { | ||||
| 	return 0; /* do nothing */ | ||||
| } | ||||
| 
 | ||||
| static struct page *dummy_pcm_page(struct snd_pcm_substream *substream, | ||||
| 				   unsigned long offset) | ||||
| { | ||||
| 	return virt_to_page(dummy_page[substream->stream]); /* the same page */ | ||||
| } | ||||
| 
 | ||||
| static struct snd_pcm_ops dummy_pcm_ops = { | ||||
| 	.open =		dummy_pcm_open, | ||||
| 	.close =	dummy_pcm_close, | ||||
| 	.ioctl =	snd_pcm_lib_ioctl, | ||||
| 	.hw_params =	dummy_pcm_hw_params, | ||||
| 	.hw_free =	dummy_pcm_hw_free, | ||||
| 	.prepare =	dummy_pcm_prepare, | ||||
| 	.trigger =	dummy_pcm_trigger, | ||||
| 	.pointer =	dummy_pcm_pointer, | ||||
| }; | ||||
| 
 | ||||
| static struct snd_pcm_ops snd_card_dummy_capture_ops = { | ||||
| 	.open =			snd_card_dummy_capture_open, | ||||
| 	.close =		snd_card_dummy_capture_close, | ||||
| 	.ioctl =		snd_pcm_lib_ioctl, | ||||
| 	.hw_params =		snd_card_dummy_hw_params, | ||||
| 	.hw_free =		snd_card_dummy_hw_free, | ||||
| 	.prepare =		snd_card_dummy_pcm_prepare, | ||||
| 	.trigger =		snd_card_dummy_pcm_trigger, | ||||
| 	.pointer =		snd_card_dummy_pcm_pointer, | ||||
| static struct snd_pcm_ops dummy_pcm_ops_no_buf = { | ||||
| 	.open =		dummy_pcm_open, | ||||
| 	.close =	dummy_pcm_close, | ||||
| 	.ioctl =	snd_pcm_lib_ioctl, | ||||
| 	.hw_params =	dummy_pcm_hw_params, | ||||
| 	.hw_free =	dummy_pcm_hw_free, | ||||
| 	.prepare =	dummy_pcm_prepare, | ||||
| 	.trigger =	dummy_pcm_trigger, | ||||
| 	.pointer =	dummy_pcm_pointer, | ||||
| 	.copy =		dummy_pcm_copy, | ||||
| 	.silence =	dummy_pcm_silence, | ||||
| 	.page =		dummy_pcm_page, | ||||
| }; | ||||
| 
 | ||||
| static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, | ||||
| 					int substreams) | ||||
| { | ||||
| 	struct snd_pcm *pcm; | ||||
| 	struct snd_pcm_ops *ops; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = snd_pcm_new(dummy->card, "Dummy PCM", device, | ||||
|  | @ -440,17 +669,28 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, | |||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	dummy->pcm = pcm; | ||||
| 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); | ||||
| 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops); | ||||
| 	if (fake_buffer) | ||||
| 		ops = &dummy_pcm_ops_no_buf; | ||||
| 	else | ||||
| 		ops = &dummy_pcm_ops; | ||||
| 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ops); | ||||
| 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops); | ||||
| 	pcm->private_data = dummy; | ||||
| 	pcm->info_flags = 0; | ||||
| 	strcpy(pcm->name, "Dummy PCM"); | ||||
| 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | ||||
| 					      snd_dma_continuous_data(GFP_KERNEL), | ||||
| 					      0, 64*1024); | ||||
| 	if (!fake_buffer) { | ||||
| 		snd_pcm_lib_preallocate_pages_for_all(pcm, | ||||
| 			SNDRV_DMA_TYPE_CONTINUOUS, | ||||
| 			snd_dma_continuous_data(GFP_KERNEL), | ||||
| 			0, 64*1024); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * mixer interface | ||||
|  */ | ||||
| 
 | ||||
| #define DUMMY_VOLUME(xname, xindex, addr) \ | ||||
| { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||||
|   .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ | ||||
|  | @ -581,6 +821,131 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS) | ||||
| /*
 | ||||
|  * proc interface | ||||
|  */ | ||||
| static void print_formats(struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { | ||||
| 		if (dummy_pcm_hardware.formats & (1ULL << i)) | ||||
| 			snd_iprintf(buffer, " %s", snd_pcm_format_name(i)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void print_rates(struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	static int rates[] = { | ||||
| 		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, | ||||
| 		64000, 88200, 96000, 176400, 192000, | ||||
| 	}; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_CONTINUOUS) | ||||
| 		snd_iprintf(buffer, " continuous"); | ||||
| 	if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_KNOT) | ||||
| 		snd_iprintf(buffer, " knot"); | ||||
| 	for (i = 0; i < ARRAY_SIZE(rates); i++) | ||||
| 		if (dummy_pcm_hardware.rates & (1 << i)) | ||||
| 			snd_iprintf(buffer, " %d", rates[i]); | ||||
| } | ||||
| 
 | ||||
| #define get_dummy_int_ptr(ofs) \ | ||||
| 	(unsigned int *)((char *)&dummy_pcm_hardware + (ofs)) | ||||
| #define get_dummy_ll_ptr(ofs) \ | ||||
| 	(unsigned long long *)((char *)&dummy_pcm_hardware + (ofs)) | ||||
| 
 | ||||
| struct dummy_hw_field { | ||||
| 	const char *name; | ||||
| 	const char *format; | ||||
| 	unsigned int offset; | ||||
| 	unsigned int size; | ||||
| }; | ||||
| #define FIELD_ENTRY(item, fmt) {		   \ | ||||
| 	.name = #item,				   \ | ||||
| 	.format = fmt,				   \ | ||||
| 	.offset = offsetof(struct snd_pcm_hardware, item), \ | ||||
| 	.size = sizeof(dummy_pcm_hardware.item) } | ||||
| 
 | ||||
| static struct dummy_hw_field fields[] = { | ||||
| 	FIELD_ENTRY(formats, "%#llx"), | ||||
| 	FIELD_ENTRY(rates, "%#x"), | ||||
| 	FIELD_ENTRY(rate_min, "%d"), | ||||
| 	FIELD_ENTRY(rate_max, "%d"), | ||||
| 	FIELD_ENTRY(channels_min, "%d"), | ||||
| 	FIELD_ENTRY(channels_max, "%d"), | ||||
| 	FIELD_ENTRY(buffer_bytes_max, "%ld"), | ||||
| 	FIELD_ENTRY(period_bytes_min, "%ld"), | ||||
| 	FIELD_ENTRY(period_bytes_max, "%ld"), | ||||
| 	FIELD_ENTRY(periods_min, "%d"), | ||||
| 	FIELD_ENTRY(periods_max, "%d"), | ||||
| }; | ||||
| 
 | ||||
| static void dummy_proc_read(struct snd_info_entry *entry, | ||||
| 			    struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(fields); i++) { | ||||
| 		snd_iprintf(buffer, "%s ", fields[i].name); | ||||
| 		if (fields[i].size == sizeof(int)) | ||||
| 			snd_iprintf(buffer, fields[i].format, | ||||
| 				    *get_dummy_int_ptr(fields[i].offset)); | ||||
| 		else | ||||
| 			snd_iprintf(buffer, fields[i].format, | ||||
| 				    *get_dummy_ll_ptr(fields[i].offset)); | ||||
| 		if (!strcmp(fields[i].name, "formats")) | ||||
| 			print_formats(buffer); | ||||
| 		else if (!strcmp(fields[i].name, "rates")) | ||||
| 			print_rates(buffer); | ||||
| 		snd_iprintf(buffer, "\n"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void dummy_proc_write(struct snd_info_entry *entry, | ||||
| 			     struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	char line[64]; | ||||
| 
 | ||||
| 	while (!snd_info_get_line(buffer, line, sizeof(line))) { | ||||
| 		char item[20]; | ||||
| 		const char *ptr; | ||||
| 		unsigned long long val; | ||||
| 		int i; | ||||
| 
 | ||||
| 		ptr = snd_info_get_str(item, line, sizeof(item)); | ||||
| 		for (i = 0; i < ARRAY_SIZE(fields); i++) { | ||||
| 			if (!strcmp(item, fields[i].name)) | ||||
| 				break; | ||||
| 		} | ||||
| 		if (i >= ARRAY_SIZE(fields)) | ||||
| 			continue; | ||||
| 		snd_info_get_str(item, ptr, sizeof(item)); | ||||
| 		if (strict_strtoull(item, 0, &val)) | ||||
| 			continue; | ||||
| 		if (fields[i].size == sizeof(int)) | ||||
| 			*get_dummy_int_ptr(fields[i].offset) = val; | ||||
| 		else | ||||
| 			*get_dummy_ll_ptr(fields[i].offset) = val; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void __devinit dummy_proc_init(struct snd_dummy *chip) | ||||
| { | ||||
| 	struct snd_info_entry *entry; | ||||
| 
 | ||||
| 	if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) { | ||||
| 		snd_info_set_text_ops(entry, chip, dummy_proc_read); | ||||
| 		entry->c.text.write = dummy_proc_write; | ||||
| 		entry->mode |= S_IWUSR; | ||||
| 	} | ||||
| } | ||||
| #else | ||||
| #define dummy_proc_init(x) | ||||
| #endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */ | ||||
| 
 | ||||
| static int __devinit snd_dummy_probe(struct platform_device *devptr) | ||||
| { | ||||
| 	struct snd_card *card; | ||||
|  | @ -610,6 +975,8 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr) | |||
| 	strcpy(card->shortname, "Dummy"); | ||||
| 	sprintf(card->longname, "Dummy %i", dev + 1); | ||||
| 
 | ||||
| 	dummy_proc_init(dummy); | ||||
| 
 | ||||
| 	snd_card_set_dev(card, &devptr->dev); | ||||
| 
 | ||||
| 	err = snd_card_register(card); | ||||
|  | @ -670,6 +1037,7 @@ static void snd_dummy_unregister_all(void) | |||
| 	for (i = 0; i < ARRAY_SIZE(devices); ++i) | ||||
| 		platform_device_unregister(devices[i]); | ||||
| 	platform_driver_unregister(&snd_dummy_driver); | ||||
| 	free_fake_buffer(); | ||||
| } | ||||
| 
 | ||||
| static int __init alsa_card_dummy_init(void) | ||||
|  | @ -680,6 +1048,12 @@ static int __init alsa_card_dummy_init(void) | |||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 
 | ||||
| 	err = alloc_fake_buffer(); | ||||
| 	if (err < 0) { | ||||
| 		platform_driver_unregister(&snd_dummy_driver); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	cards = 0; | ||||
| 	for (i = 0; i < SNDRV_CARDS; i++) { | ||||
| 		struct platform_device *device; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| /*
 | ||||
|  *  Driver for C-Media's CMI8330 soundcards. | ||||
|  *  Driver for C-Media's CMI8330 and CMI8329 soundcards. | ||||
|  *  Copyright (c) by George Talusan <gstalusan@uwaterloo.ca> | ||||
|  *    http://www.undergrad.math.uwaterloo.ca/~gstalusa
 | ||||
|  * | ||||
|  | @ -35,7 +35,7 @@ | |||
|  * | ||||
|  *  This card has two mixers and two PCM devices.  I've cheesed it such | ||||
|  *  that recording and playback can be done through the same device. | ||||
|  *  The driver "magically" routes the capturing to the CMI8330 codec, | ||||
|  *  The driver "magically" routes the capturing to the AD1848 codec, | ||||
|  *  and playback to the SB16 codec.  This allows for full-duplex mode | ||||
|  *  to some extent. | ||||
|  *  The utilities in alsa-utils are aware of both devices, so passing | ||||
|  | @ -64,7 +64,7 @@ | |||
| /*
 | ||||
|  */ | ||||
| MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>"); | ||||
| MODULE_DESCRIPTION("C-Media CMI8330"); | ||||
| MODULE_DESCRIPTION("C-Media CMI8330/CMI8329"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}"); | ||||
| 
 | ||||
|  | @ -86,38 +86,38 @@ static long mpuport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | |||
| static int mpuirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; | ||||
| 
 | ||||
| module_param_array(index, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard."); | ||||
| MODULE_PARM_DESC(index, "Index value for CMI8330/CMI8329 soundcard."); | ||||
| module_param_array(id, charp, NULL, 0444); | ||||
| MODULE_PARM_DESC(id, "ID string  for CMI8330 soundcard."); | ||||
| MODULE_PARM_DESC(id, "ID string  for CMI8330/CMI8329 soundcard."); | ||||
| module_param_array(enable, bool, NULL, 0444); | ||||
| MODULE_PARM_DESC(enable, "Enable CMI8330 soundcard."); | ||||
| MODULE_PARM_DESC(enable, "Enable CMI8330/CMI8329 soundcard."); | ||||
| #ifdef CONFIG_PNP | ||||
| module_param_array(isapnp, bool, NULL, 0444); | ||||
| MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); | ||||
| #endif | ||||
| 
 | ||||
| module_param_array(sbport, long, NULL, 0444); | ||||
| MODULE_PARM_DESC(sbport, "Port # for CMI8330 SB driver."); | ||||
| MODULE_PARM_DESC(sbport, "Port # for CMI8330/CMI8329 SB driver."); | ||||
| module_param_array(sbirq, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330 SB driver."); | ||||
| MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330/CMI8329 SB driver."); | ||||
| module_param_array(sbdma8, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330 SB driver."); | ||||
| MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330/CMI8329 SB driver."); | ||||
| module_param_array(sbdma16, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330 SB driver."); | ||||
| MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330/CMI8329 SB driver."); | ||||
| 
 | ||||
| module_param_array(wssport, long, NULL, 0444); | ||||
| MODULE_PARM_DESC(wssport, "Port # for CMI8330 WSS driver."); | ||||
| MODULE_PARM_DESC(wssport, "Port # for CMI8330/CMI8329 WSS driver."); | ||||
| module_param_array(wssirq, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330 WSS driver."); | ||||
| MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330/CMI8329 WSS driver."); | ||||
| module_param_array(wssdma, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver."); | ||||
| MODULE_PARM_DESC(wssdma, "DMA for CMI8330/CMI8329 WSS driver."); | ||||
| 
 | ||||
| module_param_array(fmport, long, NULL, 0444); | ||||
| MODULE_PARM_DESC(fmport, "FM port # for CMI8330 driver."); | ||||
| MODULE_PARM_DESC(fmport, "FM port # for CMI8330/CMI8329 driver."); | ||||
| module_param_array(mpuport, long, NULL, 0444); | ||||
| MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330 driver."); | ||||
| MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330/CMI8329 driver."); | ||||
| module_param_array(mpuirq, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330 MPU-401 port."); | ||||
| MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330/CMI8329 MPU-401 port."); | ||||
| #ifdef CONFIG_PNP | ||||
| static int isa_registered; | ||||
| static int pnp_registered; | ||||
|  | @ -156,6 +156,11 @@ static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] = | |||
| 
 | ||||
| typedef int (*snd_pcm_open_callback_t)(struct snd_pcm_substream *); | ||||
| 
 | ||||
| enum card_type { | ||||
| 	CMI8330, | ||||
| 	CMI8329 | ||||
| }; | ||||
| 
 | ||||
| struct snd_cmi8330 { | ||||
| #ifdef CONFIG_PNP | ||||
| 	struct pnp_dev *cap; | ||||
|  | @ -172,11 +177,14 @@ struct snd_cmi8330 { | |||
| 		snd_pcm_open_callback_t open; | ||||
| 		void *private_data; /* sb or wss */ | ||||
| 	} streams[2]; | ||||
| 
 | ||||
| 	enum card_type type; | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_PNP | ||||
| 
 | ||||
| static struct pnp_card_device_id snd_cmi8330_pnpids[] = { | ||||
| 	{ .id = "CMI0001", .devs = { { "@X@0001" }, { "@@@0001" }, { "@H@0001" }, { "A@@0001" } } }, | ||||
| 	{ .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } }, | ||||
| 	{ .id = "" } | ||||
| }; | ||||
|  | @ -304,7 +312,7 @@ static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 | |||
| 	unsigned int idx; | ||||
| 	int err; | ||||
| 
 | ||||
| 	strcpy(card->mixername, "CMI8330/C3D"); | ||||
| 	strcpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); | ||||
| 
 | ||||
| 	for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) { | ||||
| 		err = snd_ctl_add(card, | ||||
|  | @ -329,6 +337,9 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard, | |||
| 	struct pnp_dev *pdev; | ||||
| 	int err; | ||||
| 
 | ||||
| 	/* CMI8329 has a device with ID A@@0001, CMI8330 does not */ | ||||
| 	acard->type = (id->devs[3].id[0]) ? CMI8329 : CMI8330; | ||||
| 
 | ||||
| 	acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL); | ||||
| 	if (acard->cap == NULL) | ||||
| 		return -EBUSY; | ||||
|  | @ -345,38 +356,45 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard, | |||
| 
 | ||||
| 	err = pnp_activate_dev(pdev); | ||||
| 	if (err < 0) { | ||||
| 		snd_printk(KERN_ERR "CMI8330/C3D PnP configure failure\n"); | ||||
| 		snd_printk(KERN_ERR "AD1848 PnP configure failure\n"); | ||||
| 		return -EBUSY; | ||||
| 	} | ||||
| 	wssport[dev] = pnp_port_start(pdev, 0); | ||||
| 	wssdma[dev] = pnp_dma(pdev, 0); | ||||
| 	wssirq[dev] = pnp_irq(pdev, 0); | ||||
| 	fmport[dev] = pnp_port_start(pdev, 1); | ||||
| 	if (pnp_port_start(pdev, 1)) | ||||
| 		fmport[dev] = pnp_port_start(pdev, 1); | ||||
| 
 | ||||
| 	/* allocate SB16 resources */ | ||||
| 	pdev = acard->play; | ||||
| 
 | ||||
| 	err = pnp_activate_dev(pdev); | ||||
| 	if (err < 0) { | ||||
| 		snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP configure failure\n"); | ||||
| 		snd_printk(KERN_ERR "SB16 PnP configure failure\n"); | ||||
| 		return -EBUSY; | ||||
| 	} | ||||
| 	sbport[dev] = pnp_port_start(pdev, 0); | ||||
| 	sbdma8[dev] = pnp_dma(pdev, 0); | ||||
| 	sbdma16[dev] = pnp_dma(pdev, 1); | ||||
| 	sbirq[dev] = pnp_irq(pdev, 0); | ||||
| 	/* On CMI8239, the OPL3 port might be present in SB16 PnP resources */ | ||||
| 	if (fmport[dev] == SNDRV_AUTO_PORT) { | ||||
| 		if (pnp_port_start(pdev, 1)) | ||||
| 			fmport[dev] = pnp_port_start(pdev, 1); | ||||
| 		else | ||||
| 			fmport[dev] = 0x388;	/* Or hardwired */ | ||||
| 	} | ||||
| 
 | ||||
| 	/* allocate MPU-401 resources */ | ||||
| 	pdev = acard->mpu; | ||||
| 
 | ||||
| 	err = pnp_activate_dev(pdev); | ||||
| 	if (err < 0) { | ||||
| 		snd_printk(KERN_ERR | ||||
| 			   "CMI8330/C3D (MPU-401) PnP configure failure\n"); | ||||
| 		return -EBUSY; | ||||
| 	if (err < 0) | ||||
| 		snd_printk(KERN_ERR "MPU-401 PnP configure failure: will be disabled\n"); | ||||
| 	else { | ||||
| 		mpuport[dev] = pnp_port_start(pdev, 0); | ||||
| 		mpuirq[dev] = pnp_irq(pdev, 0); | ||||
| 	} | ||||
| 	mpuport[dev] = pnp_port_start(pdev, 0); | ||||
| 	mpuirq[dev] = pnp_irq(pdev, 0); | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
|  | @ -430,9 +448,9 @@ static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 * | |||
| 		snd_cmi8330_capture_open | ||||
| 	}; | ||||
| 
 | ||||
| 	if ((err = snd_pcm_new(card, "CMI8330", 0, 1, 1, &pcm)) < 0) | ||||
| 	if ((err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm)) < 0) | ||||
| 		return err; | ||||
| 	strcpy(pcm->name, "CMI8330"); | ||||
| 	strcpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330"); | ||||
| 	pcm->private_data = chip; | ||||
| 	 | ||||
| 	/* SB16 */ | ||||
|  | @ -527,11 +545,11 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev) | |||
| 			     wssdma[dev], -1, | ||||
| 			     WSS_HW_DETECT, 0, &acard->wss); | ||||
| 	if (err < 0) { | ||||
| 		snd_printk(KERN_ERR PFX "(CMI8330) device busy??\n"); | ||||
| 		snd_printk(KERN_ERR PFX "AD1848 device busy??\n"); | ||||
| 		return err; | ||||
| 	} | ||||
| 	if (acard->wss->hardware != WSS_HW_CMI8330) { | ||||
| 		snd_printk(KERN_ERR PFX "(CMI8330) not found during probe\n"); | ||||
| 		snd_printk(KERN_ERR PFX "AD1848 not found during probe\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -541,11 +559,11 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev) | |||
| 				    sbdma8[dev], | ||||
| 				    sbdma16[dev], | ||||
| 				    SB_HW_AUTO, &acard->sb)) < 0) { | ||||
| 		snd_printk(KERN_ERR PFX "(SB16) device busy??\n"); | ||||
| 		snd_printk(KERN_ERR PFX "SB16 device busy??\n"); | ||||
| 		return err; | ||||
| 	} | ||||
| 	if (acard->sb->hardware != SB_HW_16) { | ||||
| 		snd_printk(KERN_ERR PFX "(SB16) not found during probe\n"); | ||||
| 		snd_printk(KERN_ERR PFX "SB16 not found during probe\n"); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -585,8 +603,8 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev) | |||
| 				mpuport[dev]); | ||||
| 	} | ||||
| 
 | ||||
| 	strcpy(card->driver, "CMI8330/C3D"); | ||||
| 	strcpy(card->shortname, "C-Media CMI8330/C3D"); | ||||
| 	strcpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); | ||||
| 	strcpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D"); | ||||
| 	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", | ||||
| 		card->shortname, | ||||
| 		acard->wss->port, | ||||
|  |  | |||
|  | @ -127,15 +127,16 @@ static void midi_poll(unsigned long dummy) | |||
| 		for (dev = 0; dev < num_midis; dev++) | ||||
| 			if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL) | ||||
| 			{ | ||||
| 				int ok = 1; | ||||
| 
 | ||||
| 				while (DATA_AVAIL(midi_out_buf[dev]) && ok) | ||||
| 				while (DATA_AVAIL(midi_out_buf[dev])) | ||||
| 				{ | ||||
| 					int ok; | ||||
| 					int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head]; | ||||
| 
 | ||||
| 					spin_unlock_irqrestore(&lock,flags);/* Give some time to others */ | ||||
| 					ok = midi_devs[dev]->outputc(dev, c); | ||||
| 					spin_lock_irqsave(&lock, flags); | ||||
| 					if (!ok) | ||||
| 						break; | ||||
| 					midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE; | ||||
| 					midi_out_buf[dev]->len--; | ||||
| 				} | ||||
|  |  | |||
|  | @ -628,7 +628,7 @@ static void li_setup_dma(dma_chan_t *chan, | |||
| 	ASSERT(!(buffer_paddr & 0xFF)); | ||||
| 	chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8); | ||||
| 
 | ||||
| 	chan->cfgval = (!LI_CCFG_LOCK | | ||||
| 	chan->cfgval = ((chan->cfgval & ~LI_CCFG_LOCK) | | ||||
| 			SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) | | ||||
| 			desc->direction | | ||||
| 			mode | | ||||
|  | @ -638,9 +638,9 @@ static void li_setup_dma(dma_chan_t *chan, | |||
| 	tmask = 13 - fragshift;		/* See Lithium DMA Notes above. */ | ||||
| 	ASSERT(size >= 2 && size <= 7); | ||||
| 	ASSERT(tmask >= 1 && tmask <= 7); | ||||
| 	chan->ctlval = (!LI_CCTL_RESET | | ||||
| 	chan->ctlval = ((chan->ctlval & ~LI_CCTL_RESET) | | ||||
| 			SHIFT_FIELD(size, LI_CCTL_SIZE) | | ||||
| 			!LI_CCTL_DMA_ENABLE | | ||||
| 			(chan->ctlval & ~LI_CCTL_DMA_ENABLE) | | ||||
| 			SHIFT_FIELD(tmask, LI_CCTL_TMASK) | | ||||
| 			SHIFT_FIELD(0, LI_CCTL_TPTR)); | ||||
| 
 | ||||
|  |  | |||
|  | @ -135,11 +135,11 @@ config SND_AW2 | |||
| 
 | ||||
| 
 | ||||
| config SND_AZT3328 | ||||
| 	tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)" | ||||
| 	depends on EXPERIMENTAL | ||||
| 	tristate "Aztech AZF3328 / PCI168" | ||||
| 	select SND_OPL3_LIB | ||||
| 	select SND_MPU401_UART | ||||
| 	select SND_PCM | ||||
| 	select SND_RAWMIDI | ||||
| 	help | ||||
| 	  Say Y here to include support for Aztech AZF3328 (PCI168) | ||||
| 	  soundcards. | ||||
|  |  | |||
|  | @ -478,45 +478,6 @@ static int snd_ali_reset_5451(struct snd_ali *codec) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #ifdef CODEC_RESET | ||||
| 
 | ||||
| static int snd_ali_reset_codec(struct snd_ali *codec) | ||||
| { | ||||
| 	struct pci_dev *pci_dev; | ||||
| 	unsigned char bVal; | ||||
| 	unsigned int   dwVal; | ||||
| 	unsigned short wCount, wReg; | ||||
| 
 | ||||
| 	pci_dev = codec->pci_m1533; | ||||
| 	 | ||||
| 	pci_read_config_dword(pci_dev, 0x7c, &dwVal); | ||||
| 	pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); | ||||
| 	udelay(5000); | ||||
| 	pci_read_config_dword(pci_dev, 0x7c, &dwVal); | ||||
| 	pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); | ||||
| 	udelay(5000); | ||||
| 
 | ||||
| 	bVal = inb(ALI_REG(codec,ALI_SCTRL)); | ||||
| 	bVal |= 0x02; | ||||
| 	outb(ALI_REG(codec,ALI_SCTRL),bVal); | ||||
| 	udelay(5000); | ||||
| 	bVal = inb(ALI_REG(codec,ALI_SCTRL)); | ||||
| 	bVal &= 0xfd; | ||||
| 	outb(ALI_REG(codec,ALI_SCTRL),bVal); | ||||
| 	udelay(15000); | ||||
| 
 | ||||
| 	wCount = 200; | ||||
| 	while (wCount--) { | ||||
| 		wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN); | ||||
| 		if ((wReg & 0x000f) == 0x000f) | ||||
| 			return 0; | ||||
| 		udelay(5000); | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  *  ALI 5451 Controller | ||||
|  */ | ||||
|  | @ -561,22 +522,6 @@ static void snd_ali_disable_address_interrupt(struct snd_ali *codec) | |||
| 	outl(gc, ALI_REG(codec, ALI_GC_CIR)); | ||||
| } | ||||
| 
 | ||||
| #if 0 /* not used */
 | ||||
| static void snd_ali_enable_voice_irq(struct snd_ali *codec, | ||||
| 				     unsigned int channel) | ||||
| { | ||||
| 	unsigned int mask; | ||||
| 	struct snd_ali_channel_control *pchregs = &(codec->chregs); | ||||
| 
 | ||||
| 	snd_ali_printk("enable_voice_irq channel=%d\n",channel); | ||||
| 	 | ||||
| 	mask = 1 << (channel & 0x1f); | ||||
| 	pchregs->data.ainten  = inl(ALI_REG(codec, pchregs->regs.ainten)); | ||||
| 	pchregs->data.ainten |= mask; | ||||
| 	outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten)); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static void snd_ali_disable_voice_irq(struct snd_ali *codec, | ||||
| 				      unsigned int channel) | ||||
| { | ||||
|  | @ -677,16 +622,6 @@ static void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| #if 0 /* not used */
 | ||||
| static void snd_ali_start_voice(struct snd_ali *codec, unsigned int channel) | ||||
| { | ||||
| 	unsigned int mask = 1 << (channel & 0x1f); | ||||
| 	 | ||||
| 	snd_ali_printk("start_voice: channel=%d\n",channel); | ||||
| 	outl(mask, ALI_REG(codec,codec->chregs.regs.start)); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel) | ||||
| { | ||||
| 	unsigned int mask = 1 << (channel & 0x1f); | ||||
|  |  | |||
							
								
								
									
										1110
									
								
								sound/pci/azt3328.c
									
										
									
									
									
								
							
							
						
						
									
										1110
									
								
								sound/pci/azt3328.c
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -6,50 +6,59 @@ | |||
| 
 | ||||
| /*** main I/O area port indices ***/ | ||||
| /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */ | ||||
| #define AZF_IO_SIZE_CODEC	0x80 | ||||
| #define AZF_IO_SIZE_CODEC_PM	0x70 | ||||
| #define AZF_IO_SIZE_CTRL	0x80 | ||||
| #define AZF_IO_SIZE_CTRL_PM	0x70 | ||||
| 
 | ||||
| /* the driver initialisation suggests a layout of 4 main areas:
 | ||||
|  * from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??). | ||||
| /* the driver initialisation suggests a layout of 4 areas
 | ||||
|  * within the main card control I/O: | ||||
|  * from 0x00 (playback codec), from 0x20 (recording codec) | ||||
|  * and from 0x40 (most certainly I2S out codec). | ||||
|  * And another area from 0x60 to 0x6f (DirectX timer, IRQ management, | ||||
|  * power management etc.???). */ | ||||
| 
 | ||||
| /** playback area **/ | ||||
| #define IDX_IO_PLAY_FLAGS       0x00 /* PU:0x0000 */ | ||||
| #define AZF_IO_OFFS_CODEC_PLAYBACK	0x00 | ||||
| #define AZF_IO_OFFS_CODEC_CAPTURE	0x20 | ||||
| #define AZF_IO_OFFS_CODEC_I2S_OUT	0x40 | ||||
| 
 | ||||
| #define IDX_IO_CODEC_DMA_FLAGS       0x00 /* PU:0x0000 */ | ||||
|      /* able to reactivate output after output muting due to 8/16bit
 | ||||
|       * output change, just like 0x0002. | ||||
|       * 0x0001 is the only bit that's able to start the DMA counter */ | ||||
|   #define DMA_RESUME			0x0001 /* paused if cleared ? */ | ||||
|   #define DMA_RESUME			0x0001 /* paused if cleared? */ | ||||
|      /* 0x0002 *temporarily* set during DMA stopping. hmm
 | ||||
|       * both 0x0002 and 0x0004 set in playback setup. */ | ||||
|      /* able to reactivate output after output muting due to 8/16bit
 | ||||
|       * output change, just like 0x0001. */ | ||||
|   #define DMA_PLAY_SOMETHING1		0x0002 /* \ alternated (toggled) */ | ||||
|   #define DMA_RUN_SOMETHING1		0x0002 /* \ alternated (toggled) */ | ||||
|      /* 0x0004: NOT able to reactivate output */ | ||||
|   #define DMA_PLAY_SOMETHING2		0x0004 /* / bits */ | ||||
|   #define DMA_RUN_SOMETHING2		0x0004 /* / bits */ | ||||
|   #define SOMETHING_ALMOST_ALWAYS_SET	0x0008 /* ???; can be modified */ | ||||
|   #define DMA_EPILOGUE_SOMETHING	0x0010 | ||||
|   #define DMA_SOMETHING_ELSE		0x0020 /* ??? */ | ||||
|   #define SOMETHING_UNMODIFIABLE	0xffc0 /* unused ? not modifiable */ | ||||
| #define IDX_IO_PLAY_IRQTYPE     0x02 /* PU:0x0001 */ | ||||
|   #define SOMETHING_UNMODIFIABLE	0xffc0 /* unused? not modifiable */ | ||||
| #define IDX_IO_CODEC_IRQTYPE     0x02 /* PU:0x0001 */ | ||||
|   /* write back to flags in case flags are set, in order to ACK IRQ in handler
 | ||||
|    * (bit 1 of port 0x64 indicates interrupt for one of these three types) | ||||
|    * sometimes in this case it just writes 0xffff to globally ACK all IRQs | ||||
|    * settings written are not reflected when reading back, though. | ||||
|    * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows ? */ | ||||
|   #define IRQ_PLAY_SOMETHING		0x0001 /* something & ACK */ | ||||
|   #define IRQ_FINISHED_PLAYBUF_1	0x0002 /* 1st dmabuf finished & ACK */ | ||||
|   #define IRQ_FINISHED_PLAYBUF_2	0x0004 /* 2nd dmabuf finished & ACK */ | ||||
|    * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows? */ | ||||
|   #define IRQ_SOMETHING			0x0001 /* something & ACK */ | ||||
|   #define IRQ_FINISHED_DMABUF_1		0x0002 /* 1st dmabuf finished & ACK */ | ||||
|   #define IRQ_FINISHED_DMABUF_2		0x0004 /* 2nd dmabuf finished & ACK */ | ||||
|   #define IRQMASK_SOME_STATUS_1		0x0008 /* \ related bits */ | ||||
|   #define IRQMASK_SOME_STATUS_2		0x0010 /* / (checked together in loop) */ | ||||
|   #define IRQMASK_UNMODIFIABLE		0xffe0 /* unused ? not modifiable */ | ||||
| #define IDX_IO_PLAY_DMA_START_1 0x04 /* start address of 1st DMA play area, PU:0x00000000 */ | ||||
| #define IDX_IO_PLAY_DMA_START_2 0x08 /* start address of 2nd DMA play area, PU:0x00000000 */ | ||||
| #define IDX_IO_PLAY_DMA_LEN_1   0x0c /* length of 1st DMA play area, PU:0x0000 */ | ||||
| #define IDX_IO_PLAY_DMA_LEN_2   0x0e /* length of 2nd DMA play area, PU:0x0000 */ | ||||
| #define IDX_IO_PLAY_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */ | ||||
| #define IDX_IO_PLAY_DMA_CURROFS	0x14 /* offset within current DMA play area, PU:0x0000 */ | ||||
| #define IDX_IO_PLAY_SOUNDFORMAT 0x16 /* PU:0x0010 */ | ||||
|   #define IRQMASK_UNMODIFIABLE		0xffe0 /* unused? not modifiable */ | ||||
|   /* start address of 1st DMA transfer area, PU:0x00000000 */ | ||||
| #define IDX_IO_CODEC_DMA_START_1 0x04 | ||||
|   /* start address of 2nd DMA transfer area, PU:0x00000000 */ | ||||
| #define IDX_IO_CODEC_DMA_START_2 0x08 | ||||
|   /* both lengths of DMA transfer areas, PU:0x00000000
 | ||||
|      length1: offset 0x0c, length2: offset 0x0e */ | ||||
| #define IDX_IO_CODEC_DMA_LENGTHS 0x0c | ||||
| #define IDX_IO_CODEC_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */ | ||||
|   /* offset within current DMA transfer area, PU:0x0000 */ | ||||
| #define IDX_IO_CODEC_DMA_CURROFS 0x14 | ||||
| #define IDX_IO_CODEC_SOUNDFORMAT 0x16 /* PU:0x0010 */ | ||||
|   /* all unspecified bits can't be modified */ | ||||
|   #define SOUNDFORMAT_FREQUENCY_MASK	0x000f | ||||
|   #define SOUNDFORMAT_XTAL1		0x00 | ||||
|  | @ -76,6 +85,7 @@ | |||
|   #define SOUNDFORMAT_FLAG_16BIT	0x0010 | ||||
|   #define SOUNDFORMAT_FLAG_2CHANNELS	0x0020 | ||||
| 
 | ||||
| 
 | ||||
| /* define frequency helpers, for maximum value safety */ | ||||
| enum azf_freq_t { | ||||
| #define AZF_FREQ(rate) AZF_FREQ_##rate = rate | ||||
|  | @ -96,29 +106,6 @@ enum azf_freq_t { | |||
| #undef AZF_FREQ | ||||
| }; | ||||
| 
 | ||||
| /** recording area (see also: playback bit flag definitions) **/ | ||||
| #define IDX_IO_REC_FLAGS	0x20 /* ??, PU:0x0000 */ | ||||
| #define IDX_IO_REC_IRQTYPE	0x22 /* ??, PU:0x0000 */ | ||||
|   #define IRQ_REC_SOMETHING		0x0001 /* something & ACK */ | ||||
|   #define IRQ_FINISHED_RECBUF_1		0x0002 /* 1st dmabuf finished & ACK */ | ||||
|   #define IRQ_FINISHED_RECBUF_2		0x0004 /* 2nd dmabuf finished & ACK */ | ||||
|   /* hmm, maybe these are just the corresponding *recording* flags ?
 | ||||
|    * but OTOH they are most likely at port 0x22 instead */ | ||||
|   #define IRQMASK_SOME_STATUS_1		0x0008 /* \ related bits */ | ||||
|   #define IRQMASK_SOME_STATUS_2		0x0010 /* / (checked together in loop) */ | ||||
| #define IDX_IO_REC_DMA_START_1  0x24 /* PU:0x00000000 */ | ||||
| #define IDX_IO_REC_DMA_START_2  0x28 /* PU:0x00000000 */ | ||||
| #define IDX_IO_REC_DMA_LEN_1    0x2c /* PU:0x0000 */ | ||||
| #define IDX_IO_REC_DMA_LEN_2    0x2e /* PU:0x0000 */ | ||||
| #define IDX_IO_REC_DMA_CURRPOS  0x30 /* PU:0x00000000 */ | ||||
| #define IDX_IO_REC_DMA_CURROFS  0x34 /* PU:0x00000000 */ | ||||
| #define IDX_IO_REC_SOUNDFORMAT  0x36 /* PU:0x0000 */ | ||||
| 
 | ||||
| /** hmm, what is this I/O area for? MPU401?? or external DAC via I2S?? (after playback, recording, ???, timer) **/ | ||||
| #define IDX_IO_SOMETHING_FLAGS	0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init, PU:0x0000 */ | ||||
| /* general */ | ||||
| #define IDX_IO_42H		0x42 /* PU:0x0001 */ | ||||
| 
 | ||||
| /** DirectX timer, main interrupt area (FIXME: and something else?) **/  | ||||
| #define IDX_IO_TIMER_VALUE	0x60 /* found this timer area by pure luck :-) */ | ||||
|   /* timer countdown value; triggers IRQ when timer is finished */ | ||||
|  | @ -133,17 +120,19 @@ enum azf_freq_t { | |||
| #define IDX_IO_IRQSTATUS        0x64 | ||||
|   /* some IRQ bit in here might also be used to signal a power-management timer
 | ||||
|    * timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing). | ||||
|    * Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which | ||||
|    * can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */ | ||||
|    * OPL3 hardware contains several timers which confusingly in most cases | ||||
|    * are NOT routed to an IRQ, but some designs (e.g. LM4560) DO support that, | ||||
|    * so I wouldn't be surprised at all to discover that AZF3328 | ||||
|    * supports that thing as well... */ | ||||
| 
 | ||||
|   #define IRQ_PLAYBACK	0x0001 | ||||
|   #define IRQ_RECORDING	0x0002 | ||||
|   #define IRQ_UNKNOWN1	0x0004 /* most probably I2S port */ | ||||
|   #define IRQ_I2S_OUT	0x0004 /* this IS I2S, right!? (untested) */ | ||||
|   #define IRQ_GAMEPORT	0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */ | ||||
|   #define IRQ_MPU401	0x0010 | ||||
|   #define IRQ_TIMER	0x0020 /* DirectX timer */ | ||||
|   #define IRQ_UNKNOWN2	0x0040 /* probably unused, or possibly I2S port? */ | ||||
|   #define IRQ_UNKNOWN3	0x0080 /* probably unused, or possibly I2S port? */ | ||||
|   #define IRQ_UNKNOWN2	0x0040 /* probably unused, or possibly OPL3 timer? */ | ||||
|   #define IRQ_UNKNOWN3	0x0080 /* probably unused, or possibly OPL3 timer? */ | ||||
| #define IDX_IO_66H		0x66    /* writing 0xffff returns 0x0000 */ | ||||
|   /* this is set to e.g. 0x3ff or 0x300, and writable;
 | ||||
|    * maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */ | ||||
|  | @ -206,7 +195,7 @@ enum azf_freq_t { | |||
| /*** Gameport area port indices ***/ | ||||
| /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */  | ||||
| #define AZF_IO_SIZE_GAME		0x08 | ||||
| #define AZF_IO_SIZE_GAME_PM	0x06 | ||||
| #define AZF_IO_SIZE_GAME_PM		0x06 | ||||
| 
 | ||||
| enum { | ||||
| 	AZF_GAME_LEGACY_IO_PORT = 0x200 | ||||
|  | @ -272,6 +261,12 @@ enum { | |||
|    * 11 --> 1/200: */ | ||||
|   #define GAME_HWCFG_ADC_COUNTER_FREQ_MASK	0x06 | ||||
| 
 | ||||
|   /* FIXME: these values might be reversed... */ | ||||
|   #define GAME_HWCFG_ADC_COUNTER_FREQ_STD	0 | ||||
|   #define GAME_HWCFG_ADC_COUNTER_FREQ_1_2	1 | ||||
|   #define GAME_HWCFG_ADC_COUNTER_FREQ_1_20	2 | ||||
|   #define GAME_HWCFG_ADC_COUNTER_FREQ_1_200	3 | ||||
| 
 | ||||
|   /* enable gameport legacy I/O address (0x200)
 | ||||
|    * I was unable to locate any configurability for a different address: */ | ||||
|   #define GAME_HWCFG_LEGACY_ADDRESS_ENABLE	0x08 | ||||
|  | @ -281,6 +276,7 @@ enum { | |||
| #define AZF_IO_SIZE_MPU_PM	0x04 | ||||
| 
 | ||||
| /*** OPL3 synth ***/ | ||||
| /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ | ||||
| #define AZF_IO_SIZE_OPL3	0x08 | ||||
| #define AZF_IO_SIZE_OPL3_PM	0x06 | ||||
| /* hmm, given that a standard OPL3 has 4 registers only,
 | ||||
|  | @ -340,4 +336,7 @@ enum { | |||
| #define SET_CHAN_LEFT	1 | ||||
| #define SET_CHAN_RIGHT	2 | ||||
| 
 | ||||
| /* helper macro to align I/O port ranges to 32bit I/O width */ | ||||
| #define AZF_ALIGN(x) (((x) + 3) & (~3)) | ||||
| 
 | ||||
| #endif /* __SOUND_AZT3328_H  */ | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ | |||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_SND_CS46XX_NEW_DSP | ||||
| #define CS46XX_MIN_PERIOD_SIZE 1 | ||||
| #define CS46XX_MIN_PERIOD_SIZE 64 | ||||
| #define CS46XX_MAX_PERIOD_SIZE 1024*1024 | ||||
| #else | ||||
| #define CS46XX_MIN_PERIOD_SIZE 2048 | ||||
|  |  | |||
|  | @ -11,9 +11,12 @@ | |||
| 
 | ||||
| 
 | ||||
| /* Timer Registers */ | ||||
| #define TIMER_TIMR          0x1B7004 | ||||
| #define INTERRUPT_GIP       0x1B7010 | ||||
| #define INTERRUPT_GIE       0x1B7014 | ||||
| #define WC		0x1b7000 | ||||
| #define TIMR		0x1b7004 | ||||
| # define	TIMR_IE		(1<<15) | ||||
| # define	TIMR_IP		(1<<14) | ||||
| #define GIP		0x1b7010 | ||||
| #define GIE		0x1b7014 | ||||
| 
 | ||||
| /* I2C Registers */ | ||||
| #define I2C_IF_ADDRESS   0x1B9000 | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) | |||
| 	hw = amixer->rsc.hw; | ||||
| 	hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE); | ||||
| 	amixer->input = rsc; | ||||
| 	if (NULL == rsc) | ||||
| 	if (!rsc) | ||||
| 		hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT); | ||||
| 	else | ||||
| 		hw->amixer_set_x(amixer->rsc.ctrl_blk, | ||||
|  | @ -99,7 +99,7 @@ static int amixer_set_sum(struct amixer *amixer, struct sum *sum) | |||
| 
 | ||||
| 	hw = amixer->rsc.hw; | ||||
| 	amixer->sum = sum; | ||||
| 	if (NULL == sum) { | ||||
| 	if (!sum) { | ||||
| 		hw->amixer_set_se(amixer->rsc.ctrl_blk, 0); | ||||
| 	} else { | ||||
| 		hw->amixer_set_se(amixer->rsc.ctrl_blk, 1); | ||||
|  | @ -124,20 +124,20 @@ static int amixer_commit_write(struct amixer *amixer) | |||
| 
 | ||||
| 	/* Program master and conjugate resources */ | ||||
| 	amixer->rsc.ops->master(&amixer->rsc); | ||||
| 	if (NULL != input) | ||||
| 	if (input) | ||||
| 		input->ops->master(input); | ||||
| 
 | ||||
| 	if (NULL != sum) | ||||
| 	if (sum) | ||||
| 		sum->rsc.ops->master(&sum->rsc); | ||||
| 
 | ||||
| 	for (i = 0; i < amixer->rsc.msr; i++) { | ||||
| 		hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk); | ||||
| 		if (NULL != input) { | ||||
| 		if (input) { | ||||
| 			hw->amixer_set_x(amixer->rsc.ctrl_blk, | ||||
| 						input->ops->output_slot(input)); | ||||
| 			input->ops->next_conj(input); | ||||
| 		} | ||||
| 		if (NULL != sum) { | ||||
| 		if (sum) { | ||||
| 			hw->amixer_set_sadr(amixer->rsc.ctrl_blk, | ||||
| 						sum->rsc.ops->index(&sum->rsc)); | ||||
| 			sum->rsc.ops->next_conj(&sum->rsc); | ||||
|  | @ -147,10 +147,10 @@ static int amixer_commit_write(struct amixer *amixer) | |||
| 		amixer->rsc.ops->next_conj(&amixer->rsc); | ||||
| 	} | ||||
| 	amixer->rsc.ops->master(&amixer->rsc); | ||||
| 	if (NULL != input) | ||||
| 	if (input) | ||||
| 		input->ops->master(input); | ||||
| 
 | ||||
| 	if (NULL != sum) | ||||
| 	if (sum) | ||||
| 		sum->rsc.ops->master(&sum->rsc); | ||||
| 
 | ||||
| 	return 0; | ||||
|  | @ -303,7 +303,7 @@ int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) | |||
| 
 | ||||
| 	*ramixer_mgr = NULL; | ||||
| 	amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL); | ||||
| 	if (NULL == amixer_mgr) | ||||
| 	if (!amixer_mgr) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw); | ||||
|  | @ -456,7 +456,7 @@ int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) | |||
| 
 | ||||
| 	*rsum_mgr = NULL; | ||||
| 	sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL); | ||||
| 	if (NULL == sum_mgr) | ||||
| 	if (!sum_mgr) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw); | ||||
|  |  | |||
|  | @ -136,7 +136,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 	struct snd_pcm_runtime *runtime; | ||||
| 	struct ct_vm *vm; | ||||
| 
 | ||||
| 	if (NULL == apcm->substream) | ||||
| 	if (!apcm->substream) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	runtime = apcm->substream->runtime; | ||||
|  | @ -144,7 +144,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 
 | ||||
| 	apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes); | ||||
| 
 | ||||
| 	if (NULL == apcm->vm_block) | ||||
| 	if (!apcm->vm_block) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	return 0; | ||||
|  | @ -154,7 +154,7 @@ static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| { | ||||
| 	struct ct_vm *vm; | ||||
| 
 | ||||
| 	if (NULL == apcm->vm_block) | ||||
| 	if (!apcm->vm_block) | ||||
| 		return; | ||||
| 
 | ||||
| 	vm = atc->vm; | ||||
|  | @ -231,16 +231,16 @@ atc_get_pitch(unsigned int input_rate, unsigned int output_rate) | |||
| 
 | ||||
| static int select_rom(unsigned int pitch) | ||||
| { | ||||
| 	if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) { | ||||
| 	if (pitch > 0x00428f5c && pitch < 0x01b851ec) { | ||||
| 		/* 0.26 <= pitch <= 1.72 */ | ||||
| 		return 1; | ||||
| 	} else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) { | ||||
| 	} else if (pitch == 0x01d66666 || pitch == 0x01d66667) { | ||||
| 		/* pitch == 1.8375 */ | ||||
| 		return 2; | ||||
| 	} else if (0x02000000 == pitch) { | ||||
| 	} else if (pitch == 0x02000000) { | ||||
| 		/* pitch == 2 */ | ||||
| 		return 3; | ||||
| 	} else if ((pitch >= 0x0) && (pitch <= 0x08000000)) { | ||||
| 	} else if (pitch >= 0x0 && pitch <= 0x08000000) { | ||||
| 		/* 0 <= pitch <= 8 */ | ||||
| 		return 0; | ||||
| 	} else { | ||||
|  | @ -283,7 +283,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 	/* Get AMIXER resource */ | ||||
| 	n_amixer = (n_amixer < 2) ? 2 : n_amixer; | ||||
| 	apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); | ||||
| 	if (NULL == apcm->amixers) { | ||||
| 	if (!apcm->amixers) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error1; | ||||
| 	} | ||||
|  | @ -311,7 +311,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 					INIT_VOL, atc->pcm[i+device*2]); | ||||
| 		mutex_unlock(&atc->atc_mutex); | ||||
| 		src = src->ops->next_interleave(src); | ||||
| 		if (NULL == src) | ||||
| 		if (!src) | ||||
| 			src = apcm->src; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -334,7 +334,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 	struct srcimp *srcimp; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (NULL != apcm->srcimps) { | ||||
| 	if (apcm->srcimps) { | ||||
| 		for (i = 0; i < apcm->n_srcimp; i++) { | ||||
| 			srcimp = apcm->srcimps[i]; | ||||
| 			srcimp->ops->unmap(srcimp); | ||||
|  | @ -345,7 +345,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 		apcm->srcimps = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != apcm->srccs) { | ||||
| 	if (apcm->srccs) { | ||||
| 		for (i = 0; i < apcm->n_srcc; i++) { | ||||
| 			src_mgr->put_src(src_mgr, apcm->srccs[i]); | ||||
| 			apcm->srccs[i] = NULL; | ||||
|  | @ -354,7 +354,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 		apcm->srccs = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != apcm->amixers) { | ||||
| 	if (apcm->amixers) { | ||||
| 		for (i = 0; i < apcm->n_amixer; i++) { | ||||
| 			amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]); | ||||
| 			apcm->amixers[i] = NULL; | ||||
|  | @ -363,17 +363,17 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 		apcm->amixers = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != apcm->mono) { | ||||
| 	if (apcm->mono) { | ||||
| 		sum_mgr->put_sum(sum_mgr, apcm->mono); | ||||
| 		apcm->mono = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != apcm->src) { | ||||
| 	if (apcm->src) { | ||||
| 		src_mgr->put_src(src_mgr, apcm->src); | ||||
| 		apcm->src = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != apcm->vm_block) { | ||||
| 	if (apcm->vm_block) { | ||||
| 		/* Undo device virtual mem map */ | ||||
| 		ct_unmap_audio_buffer(atc, apcm); | ||||
| 		apcm->vm_block = NULL; | ||||
|  | @ -419,7 +419,7 @@ static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 	src->ops->set_state(src, SRC_STATE_OFF); | ||||
| 	src->ops->commit_write(src); | ||||
| 
 | ||||
| 	if (NULL != apcm->srccs) { | ||||
| 	if (apcm->srccs) { | ||||
| 		for (i = 0; i < apcm->n_srcc; i++) { | ||||
| 			src = apcm->srccs[i]; | ||||
| 			src->ops->set_bm(src, 0); | ||||
|  | @ -544,18 +544,18 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 
 | ||||
| 	if (n_srcc) { | ||||
| 		apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL); | ||||
| 		if (NULL == apcm->srccs) | ||||
| 		if (!apcm->srccs) | ||||
| 			return -ENOMEM; | ||||
| 	} | ||||
| 	if (n_amixer) { | ||||
| 		apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); | ||||
| 		if (NULL == apcm->amixers) { | ||||
| 		if (!apcm->amixers) { | ||||
| 			err = -ENOMEM; | ||||
| 			goto error1; | ||||
| 		} | ||||
| 	} | ||||
| 	apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL); | ||||
| 	if (NULL == apcm->srcimps) { | ||||
| 	if (!apcm->srcimps) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error1; | ||||
| 	} | ||||
|  | @ -818,7 +818,7 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, | |||
| 	/* Get AMIXER resource */ | ||||
| 	n_amixer = (n_amixer < 2) ? 2 : n_amixer; | ||||
| 	apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); | ||||
| 	if (NULL == apcm->amixers) { | ||||
| 	if (!apcm->amixers) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error1; | ||||
| 	} | ||||
|  | @ -919,7 +919,7 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) | |||
| 		amixer = apcm->amixers[i]; | ||||
| 		amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); | ||||
| 		src = src->ops->next_interleave(src); | ||||
| 		if (NULL == src) | ||||
| 		if (!src) | ||||
| 			src = apcm->src; | ||||
| 	} | ||||
| 	/* Connect to SPDIFOO */ | ||||
|  | @ -1121,7 +1121,7 @@ static int atc_release_resources(struct ct_atc *atc) | |||
| 	struct ct_mixer *mixer = NULL; | ||||
| 
 | ||||
| 	/* disconnect internal mixer objects */ | ||||
| 	if (NULL != atc->mixer) { | ||||
| 	if (atc->mixer) { | ||||
| 		mixer = atc->mixer; | ||||
| 		mixer->set_input_left(mixer, MIX_LINE_IN, NULL); | ||||
| 		mixer->set_input_right(mixer, MIX_LINE_IN, NULL); | ||||
|  | @ -1131,7 +1131,7 @@ static int atc_release_resources(struct ct_atc *atc) | |||
| 		mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL); | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != atc->daios) { | ||||
| 	if (atc->daios) { | ||||
| 		daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; | ||||
| 		for (i = 0; i < atc->n_daio; i++) { | ||||
| 			daio = atc->daios[i]; | ||||
|  | @ -1149,7 +1149,7 @@ static int atc_release_resources(struct ct_atc *atc) | |||
| 		atc->daios = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != atc->pcm) { | ||||
| 	if (atc->pcm) { | ||||
| 		sum_mgr = atc->rsc_mgrs[SUM]; | ||||
| 		for (i = 0; i < atc->n_pcm; i++) | ||||
| 			sum_mgr->put_sum(sum_mgr, atc->pcm[i]); | ||||
|  | @ -1158,7 +1158,7 @@ static int atc_release_resources(struct ct_atc *atc) | |||
| 		atc->pcm = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != atc->srcs) { | ||||
| 	if (atc->srcs) { | ||||
| 		src_mgr = atc->rsc_mgrs[SRC]; | ||||
| 		for (i = 0; i < atc->n_src; i++) | ||||
| 			src_mgr->put_src(src_mgr, atc->srcs[i]); | ||||
|  | @ -1167,7 +1167,7 @@ static int atc_release_resources(struct ct_atc *atc) | |||
| 		atc->srcs = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != atc->srcimps) { | ||||
| 	if (atc->srcimps) { | ||||
| 		srcimp_mgr = atc->rsc_mgrs[SRCIMP]; | ||||
| 		for (i = 0; i < atc->n_srcimp; i++) { | ||||
| 			srcimp = atc->srcimps[i]; | ||||
|  | @ -1185,7 +1185,7 @@ static int ct_atc_destroy(struct ct_atc *atc) | |||
| { | ||||
| 	int i = 0; | ||||
| 
 | ||||
| 	if (NULL == atc) | ||||
| 	if (!atc) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (atc->timer) { | ||||
|  | @ -1196,21 +1196,20 @@ static int ct_atc_destroy(struct ct_atc *atc) | |||
| 	atc_release_resources(atc); | ||||
| 
 | ||||
| 	/* Destroy internal mixer objects */ | ||||
| 	if (NULL != atc->mixer) | ||||
| 	if (atc->mixer) | ||||
| 		ct_mixer_destroy(atc->mixer); | ||||
| 
 | ||||
| 	for (i = 0; i < NUM_RSCTYP; i++) { | ||||
| 		if ((NULL != rsc_mgr_funcs[i].destroy) && | ||||
| 		    (NULL != atc->rsc_mgrs[i])) | ||||
| 		if (rsc_mgr_funcs[i].destroy && atc->rsc_mgrs[i]) | ||||
| 			rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]); | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	if (NULL != atc->hw) | ||||
| 	if (atc->hw) | ||||
| 		destroy_hw_obj((struct hw *)atc->hw); | ||||
| 
 | ||||
| 	/* Destroy device virtual memory manager object */ | ||||
| 	if (NULL != atc->vm) { | ||||
| 	if (atc->vm) { | ||||
| 		ct_vm_destroy(atc->vm); | ||||
| 		atc->vm = NULL; | ||||
| 	} | ||||
|  | @ -1275,7 +1274,7 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) | |||
| 	alsa_dev_funcs[MIXER].public_name = atc->chip_name; | ||||
| 
 | ||||
| 	for (i = 0; i < NUM_CTALSADEVS; i++) { | ||||
| 		if (NULL == alsa_dev_funcs[i].create) | ||||
| 		if (!alsa_dev_funcs[i].create) | ||||
| 			continue; | ||||
| 
 | ||||
| 		err = alsa_dev_funcs[i].create(atc, i, | ||||
|  | @ -1312,7 +1311,7 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc) | |||
| 		return err; | ||||
| 
 | ||||
| 	for (i = 0; i < NUM_RSCTYP; i++) { | ||||
| 		if (NULL == rsc_mgr_funcs[i].create) | ||||
| 		if (!rsc_mgr_funcs[i].create) | ||||
| 			continue; | ||||
| 
 | ||||
| 		err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]); | ||||
|  | @ -1339,19 +1338,19 @@ static int atc_get_resources(struct ct_atc *atc) | |||
| 	int err, i; | ||||
| 
 | ||||
| 	atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); | ||||
| 	if (NULL == atc->daios) | ||||
| 	if (!atc->daios) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); | ||||
| 	if (NULL == atc->srcs) | ||||
| 	if (!atc->srcs) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); | ||||
| 	if (NULL == atc->srcimps) | ||||
| 	if (!atc->srcimps) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL); | ||||
| 	if (NULL == atc->pcm) | ||||
| 	if (!atc->pcm) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; | ||||
|  | @ -1648,7 +1647,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, | |||
| 	*ratc = NULL; | ||||
| 
 | ||||
| 	atc = kzalloc(sizeof(*atc), GFP_KERNEL); | ||||
| 	if (NULL == atc) | ||||
| 	if (!atc) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	/* Set operations */ | ||||
|  |  | |||
|  | @ -173,7 +173,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input) | |||
| 	int i; | ||||
| 
 | ||||
| 	entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL); | ||||
| 	if (NULL == entry) | ||||
| 	if (!entry) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	/* Program master and conjugate resources */ | ||||
|  | @ -201,7 +201,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) | |||
| 	int i; | ||||
| 
 | ||||
| 	entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL); | ||||
| 	if (NULL == entry) | ||||
| 	if (!entry) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	/* Program master and conjugate resources */ | ||||
|  | @ -228,7 +228,7 @@ static int dao_clear_left_input(struct dao *dao) | |||
| 	struct daio *daio = &dao->daio; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (NULL == dao->imappers[0]) | ||||
| 	if (!dao->imappers[0]) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	entry = dao->imappers[0]; | ||||
|  | @ -252,7 +252,7 @@ static int dao_clear_right_input(struct dao *dao) | |||
| 	struct daio *daio = &dao->daio; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (NULL == dao->imappers[daio->rscl.msr]) | ||||
| 	if (!dao->imappers[daio->rscl.msr]) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	entry = dao->imappers[daio->rscl.msr]; | ||||
|  | @ -408,7 +408,7 @@ static int dao_rsc_init(struct dao *dao, | |||
| 		return err; | ||||
| 
 | ||||
| 	dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL); | ||||
| 	if (NULL == dao->imappers) { | ||||
| 	if (!dao->imappers) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error1; | ||||
| 	} | ||||
|  | @ -442,11 +442,11 @@ error1: | |||
| 
 | ||||
| static int dao_rsc_uninit(struct dao *dao) | ||||
| { | ||||
| 	if (NULL != dao->imappers) { | ||||
| 		if (NULL != dao->imappers[0]) | ||||
| 	if (dao->imappers) { | ||||
| 		if (dao->imappers[0]) | ||||
| 			dao_clear_left_input(dao); | ||||
| 
 | ||||
| 		if (NULL != dao->imappers[dao->daio.rscl.msr]) | ||||
| 		if (dao->imappers[dao->daio.rscl.msr]) | ||||
| 			dao_clear_right_input(dao); | ||||
| 
 | ||||
| 		kfree(dao->imappers); | ||||
|  | @ -555,7 +555,7 @@ static int get_daio_rsc(struct daio_mgr *mgr, | |||
| 	/* Allocate mem for daio resource */ | ||||
| 	if (desc->type <= DAIO_OUT_MAX) { | ||||
| 		dao = kzalloc(sizeof(*dao), GFP_KERNEL); | ||||
| 		if (NULL == dao) { | ||||
| 		if (!dao) { | ||||
| 			err = -ENOMEM; | ||||
| 			goto error; | ||||
| 		} | ||||
|  | @ -566,7 +566,7 @@ static int get_daio_rsc(struct daio_mgr *mgr, | |||
| 		*rdaio = &dao->daio; | ||||
| 	} else { | ||||
| 		dai = kzalloc(sizeof(*dai), GFP_KERNEL); | ||||
| 		if (NULL == dai) { | ||||
| 		if (!dai) { | ||||
| 			err = -ENOMEM; | ||||
| 			goto error; | ||||
| 		} | ||||
|  | @ -583,9 +583,9 @@ static int get_daio_rsc(struct daio_mgr *mgr, | |||
| 	return 0; | ||||
| 
 | ||||
| error: | ||||
| 	if (NULL != dao) | ||||
| 	if (dao) | ||||
| 		kfree(dao); | ||||
| 	else if (NULL != dai) | ||||
| 	else if (dai) | ||||
| 		kfree(dai); | ||||
| 
 | ||||
| 	spin_lock_irqsave(&mgr->mgr_lock, flags); | ||||
|  | @ -663,7 +663,7 @@ static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry) | |||
| 	int err; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&mgr->imap_lock, flags); | ||||
| 	if ((0 == entry->addr) && (mgr->init_imap_added)) { | ||||
| 	if (!entry->addr && mgr->init_imap_added) { | ||||
| 		input_mapper_delete(&mgr->imappers, mgr->init_imap, | ||||
| 							daio_map_op, mgr); | ||||
| 		mgr->init_imap_added = 0; | ||||
|  | @ -707,7 +707,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) | |||
| 
 | ||||
| 	*rdaio_mgr = NULL; | ||||
| 	daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL); | ||||
| 	if (NULL == daio_mgr) | ||||
| 	if (!daio_mgr) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw); | ||||
|  | @ -718,7 +718,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) | |||
| 	spin_lock_init(&daio_mgr->imap_lock); | ||||
| 	INIT_LIST_HEAD(&daio_mgr->imappers); | ||||
| 	entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||||
| 	if (NULL == entry) { | ||||
| 	if (!entry) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error2; | ||||
| 	} | ||||
|  |  | |||
|  | @ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -723,7 +723,7 @@ static int amixer_mgr_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	/*blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk;*/ | ||||
|  | @ -909,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -958,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -1152,7 +1152,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	blk->i2sctl = hw_read_20kx(hw, I2SCTL); | ||||
|  | @ -1808,7 +1808,7 @@ static int uaa_to_xfi(struct pci_dev *pci) | |||
| 	/* By default, Hendrix card UAA Bar0 should be using memory... */ | ||||
| 	io_base = pci_resource_start(pci, 0); | ||||
| 	mem_base = ioremap(io_base, pci_resource_len(pci, 0)); | ||||
| 	if (NULL == mem_base) | ||||
| 	if (!mem_base) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	/* Read current mode from Mode Change Register */ | ||||
|  | @ -1977,7 +1977,7 @@ static int hw_card_shutdown(struct hw *hw) | |||
| 
 | ||||
| 	hw->irq	= -1; | ||||
| 
 | ||||
| 	if (NULL != ((void *)hw->mem_base)) | ||||
| 	if (hw->mem_base) | ||||
| 		iounmap((void *)hw->mem_base); | ||||
| 
 | ||||
| 	hw->mem_base = (unsigned long)NULL; | ||||
|  | @ -2274,7 +2274,7 @@ int __devinit create_20k1_hw_obj(struct hw **rhw) | |||
| 
 | ||||
| 	*rhw = NULL; | ||||
| 	hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL); | ||||
| 	if (NULL == hw20k1) | ||||
| 	if (!hw20k1) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	spin_lock_init(&hw20k1->reg_20k1_lock); | ||||
|  |  | |||
|  | @ -166,7 +166,7 @@ static int src_get_rsc_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -492,7 +492,7 @@ static int src_mgr_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -513,7 +513,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -891,7 +891,7 @@ static int dai_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -941,7 +941,7 @@ static int dao_get_ctrl_blk(void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	*rblk = blk; | ||||
|  | @ -1092,7 +1092,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) | |||
| 
 | ||||
| 	*rblk = NULL; | ||||
| 	blk = kzalloc(sizeof(*blk), GFP_KERNEL); | ||||
| 	if (NULL == blk) | ||||
| 	if (!blk) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	for (i = 0; i < 8; i++) { | ||||
|  | @ -1112,6 +1112,26 @@ static int daio_mgr_put_ctrl_blk(void *blk) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Timer interrupt */ | ||||
| static int set_timer_irq(struct hw *hw, int enable) | ||||
| { | ||||
| 	hw_write_20kx(hw, GIE, enable ? IT_INT : 0); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int set_timer_tick(struct hw *hw, unsigned int ticks) | ||||
| { | ||||
| 	if (ticks) | ||||
| 		ticks |= TIMR_IE | TIMR_IP; | ||||
| 	hw_write_20kx(hw, TIMR, ticks); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static unsigned int get_wc(struct hw *hw) | ||||
| { | ||||
| 	return hw_read_20kx(hw, WC); | ||||
| } | ||||
| 
 | ||||
| /* Card hardware initialization block */ | ||||
| struct dac_conf { | ||||
| 	unsigned int msr; /* master sample rate in rsrs */ | ||||
|  | @ -1841,6 +1861,22 @@ static int hw_have_digit_io_switch(struct hw *hw) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id) | ||||
| { | ||||
| 	struct hw *hw = dev_id; | ||||
| 	unsigned int status; | ||||
| 
 | ||||
| 	status = hw_read_20kx(hw, GIP); | ||||
| 	if (!status) | ||||
| 		return IRQ_NONE; | ||||
| 
 | ||||
| 	if (hw->irq_callback) | ||||
| 		hw->irq_callback(hw->irq_callback_data, status); | ||||
| 
 | ||||
| 	hw_write_20kx(hw, GIP, status); | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static int hw_card_start(struct hw *hw) | ||||
| { | ||||
| 	int err = 0; | ||||
|  | @ -1868,7 +1904,7 @@ static int hw_card_start(struct hw *hw) | |||
| 		hw->io_base = pci_resource_start(hw->pci, 2); | ||||
| 		hw->mem_base = (unsigned long)ioremap(hw->io_base, | ||||
| 					pci_resource_len(hw->pci, 2)); | ||||
| 		if (NULL == (void *)hw->mem_base) { | ||||
| 		if (!hw->mem_base) { | ||||
| 			err = -ENOENT; | ||||
| 			goto error2; | ||||
| 		} | ||||
|  | @ -1879,12 +1915,15 @@ static int hw_card_start(struct hw *hw) | |||
| 	set_field(&gctl, GCTL_UAA, 0); | ||||
| 	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||||
| 
 | ||||
| 	/*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED,
 | ||||
| 				atc->chip_details->nm_card, hw))) { | ||||
| 		goto error3; | ||||
| 	if (hw->irq < 0) { | ||||
| 		err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED, | ||||
| 				  "ctxfi", hw); | ||||
| 		if (err < 0) { | ||||
| 			printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); | ||||
| 			goto error2; | ||||
| 		} | ||||
| 		hw->irq = pci->irq; | ||||
| 	} | ||||
| 	hw->irq = pci->irq; | ||||
| 	*/ | ||||
| 
 | ||||
| 	pci_set_master(pci); | ||||
| 
 | ||||
|  | @ -1923,7 +1962,7 @@ static int hw_card_shutdown(struct hw *hw) | |||
| 
 | ||||
| 	hw->irq	= -1; | ||||
| 
 | ||||
| 	if (NULL != ((void *)hw->mem_base)) | ||||
| 	if (hw->mem_base) | ||||
| 		iounmap((void *)hw->mem_base); | ||||
| 
 | ||||
| 	hw->mem_base = (unsigned long)NULL; | ||||
|  | @ -1972,7 +2011,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) | |||
| 	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); | ||||
| 
 | ||||
| 	/* Reset all global pending interrupts */ | ||||
| 	hw_write_20kx(hw, INTERRUPT_GIE, 0); | ||||
| 	hw_write_20kx(hw, GIE, 0); | ||||
| 	/* Reset all SRC pending interrupts */ | ||||
| 	hw_write_20kx(hw, SRC_IP, 0); | ||||
| 
 | ||||
|  | @ -2149,6 +2188,10 @@ static struct hw ct20k2_preset __devinitdata = { | |||
| 	.daio_mgr_set_imapnxt = daio_mgr_set_imapnxt, | ||||
| 	.daio_mgr_set_imapaddr = daio_mgr_set_imapaddr, | ||||
| 	.daio_mgr_commit_write = daio_mgr_commit_write, | ||||
| 
 | ||||
| 	.set_timer_irq = set_timer_irq, | ||||
| 	.set_timer_tick = set_timer_tick, | ||||
| 	.get_wc = get_wc, | ||||
| }; | ||||
| 
 | ||||
| int __devinit create_20k2_hw_obj(struct hw **rhw) | ||||
|  |  | |||
|  | @ -654,7 +654,7 @@ ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) | |||
| 	int err; | ||||
| 
 | ||||
| 	kctl = snd_ctl_new1(new, mixer->atc); | ||||
| 	if (NULL == kctl) | ||||
| 	if (!kctl) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface) | ||||
|  | @ -837,17 +837,17 @@ static int ct_mixer_get_mem(struct ct_mixer **rmixer) | |||
| 	*rmixer = NULL; | ||||
| 	/* Allocate mem for mixer obj */ | ||||
| 	mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); | ||||
| 	if (NULL == mixer) | ||||
| 	if (!mixer) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM), | ||||
| 				 GFP_KERNEL); | ||||
| 	if (NULL == mixer->amixers) { | ||||
| 	if (!mixer->amixers) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error1; | ||||
| 	} | ||||
| 	mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL); | ||||
| 	if (NULL == mixer->sums) { | ||||
| 	if (!mixer->sums) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error2; | ||||
| 	} | ||||
|  |  | |||
|  | @ -97,7 +97,7 @@ static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm) | |||
| { | ||||
| 	struct ct_atc_pcm *apcm = atc_pcm; | ||||
| 
 | ||||
| 	if (NULL == apcm->substream) | ||||
| 	if (!apcm->substream) | ||||
| 		return; | ||||
| 
 | ||||
| 	snd_pcm_period_elapsed(apcm->substream); | ||||
|  | @ -123,7 +123,7 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) | |||
| 	int err; | ||||
| 
 | ||||
| 	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); | ||||
| 	if (NULL == apcm) | ||||
| 	if (!apcm) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	apcm->substream = substream; | ||||
|  | @ -271,7 +271,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) | |||
| 	int err; | ||||
| 
 | ||||
| 	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); | ||||
| 	if (NULL == apcm) | ||||
| 	if (!apcm) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	apcm->started = 0; | ||||
|  |  | |||
|  | @ -144,7 +144,7 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw) | |||
| 	rsc->msr = msr; | ||||
| 	rsc->hw = hw; | ||||
| 	rsc->ops = &rsc_generic_ops; | ||||
| 	if (NULL == hw) { | ||||
| 	if (!hw) { | ||||
| 		rsc->ctrl_blk = NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | @ -216,7 +216,7 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, | |||
| 	mgr->type = NUM_RSCTYP; | ||||
| 
 | ||||
| 	mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL); | ||||
| 	if (NULL == mgr->rscs) | ||||
| 	if (!mgr->rscs) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	switch (type) { | ||||
|  |  | |||
|  | @ -441,7 +441,7 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc) | |||
| 	else | ||||
| 		src = kzalloc(sizeof(*src), GFP_KERNEL); | ||||
| 
 | ||||
| 	if (NULL == src) { | ||||
| 	if (!src) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error1; | ||||
| 	} | ||||
|  | @ -550,7 +550,7 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr) | |||
| 
 | ||||
| 	*rsrc_mgr = NULL; | ||||
| 	src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL); | ||||
| 	if (NULL == src_mgr) | ||||
| 	if (!src_mgr) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw); | ||||
|  | @ -679,7 +679,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp, | |||
| 	/* Reserve memory for imapper nodes */ | ||||
| 	srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr, | ||||
| 				   GFP_KERNEL); | ||||
| 	if (NULL == srcimp->imappers) { | ||||
| 	if (!srcimp->imappers) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error1; | ||||
| 	} | ||||
|  | @ -833,7 +833,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) | |||
| 
 | ||||
| 	*rsrcimp_mgr = NULL; | ||||
| 	srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL); | ||||
| 	if (NULL == srcimp_mgr) | ||||
| 	if (!srcimp_mgr) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw); | ||||
|  | @ -844,7 +844,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) | |||
| 	spin_lock_init(&srcimp_mgr->imap_lock); | ||||
| 	INIT_LIST_HEAD(&srcimp_mgr->imappers); | ||||
| 	entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||||
| 	if (NULL == entry) { | ||||
| 	if (!entry) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto error2; | ||||
| 	} | ||||
|  |  | |||
|  | @ -60,7 +60,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size) | |||
| 	} | ||||
| 
 | ||||
| 	block = kzalloc(sizeof(*block), GFP_KERNEL); | ||||
| 	if (NULL == block) | ||||
| 	if (!block) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	block->addr = entry->addr; | ||||
|  | @ -181,7 +181,7 @@ int ct_vm_create(struct ct_vm **rvm) | |||
| 	*rvm = NULL; | ||||
| 
 | ||||
| 	vm = kzalloc(sizeof(*vm), GFP_KERNEL); | ||||
| 	if (NULL == vm) | ||||
| 	if (!vm) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	mutex_init(&vm->lock); | ||||
|  | @ -189,7 +189,7 @@ int ct_vm_create(struct ct_vm **rvm) | |||
| 	/* Allocate page table pages */ | ||||
| 	for (i = 0; i < CT_PTP_NUM; i++) { | ||||
| 		vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||||
| 		if (NULL == vm->ptp[i]) | ||||
| 		if (!vm->ptp[i]) | ||||
| 			break; | ||||
| 	} | ||||
| 	if (!i) { | ||||
|  |  | |||
|  | @ -46,6 +46,20 @@ config SND_HDA_INPUT_JACK | |||
| 	  Say Y here to enable the jack plugging notification via | ||||
| 	  input layer. | ||||
| 
 | ||||
| config SND_HDA_PATCH_LOADER | ||||
| 	bool "Support initialization patch loading for HD-audio" | ||||
| 	depends on EXPERIMENTAL | ||||
| 	select FW_LOADER | ||||
| 	select SND_HDA_HWDEP | ||||
| 	select SND_HDA_RECONFIG | ||||
| 	help | ||||
| 	  Say Y here to allow the HD-audio driver to load a pseudo | ||||
| 	  firmware file ("patch") for overriding the BIOS setup at | ||||
| 	  start up.  The "patch" file can be specified via patch module | ||||
| 	  option, such as patch=hda-init. | ||||
| 
 | ||||
| 	  This option turns on hwdep and reconfig features automatically. | ||||
| 
 | ||||
| config SND_HDA_CODEC_REALTEK | ||||
| 	bool "Build Realtek HD-audio codec support" | ||||
| 	default y | ||||
|  | @ -134,6 +148,19 @@ config SND_HDA_ELD | |||
| 	def_bool y | ||||
| 	depends on SND_HDA_CODEC_INTELHDMI | ||||
| 
 | ||||
| config SND_HDA_CODEC_CIRRUS | ||||
| 	bool "Build Cirrus Logic codec support" | ||||
| 	depends on SND_HDA_INTEL | ||||
| 	default y | ||||
| 	help | ||||
| 	  Say Y here to include Cirrus Logic codec support in | ||||
| 	  snd-hda-intel driver, such as CS4206. | ||||
| 
 | ||||
| 	  When the HD-audio driver is built as a module, the codec | ||||
| 	  support code is also built as another module, | ||||
| 	  snd-hda-codec-cirrus. | ||||
| 	  This module is automatically loaded at probing. | ||||
| 
 | ||||
| config SND_HDA_CODEC_CONEXANT | ||||
| 	bool "Build Conexant HD-audio codec support" | ||||
| 	default y | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ snd-hda-codec-analog-objs :=	patch_analog.o | |||
| snd-hda-codec-idt-objs :=	patch_sigmatel.o | ||||
| snd-hda-codec-si3054-objs :=	patch_si3054.o | ||||
| snd-hda-codec-atihdmi-objs :=	patch_atihdmi.o | ||||
| snd-hda-codec-cirrus-objs :=	patch_cirrus.o | ||||
| snd-hda-codec-ca0110-objs :=	patch_ca0110.o | ||||
| snd-hda-codec-conexant-objs :=	patch_conexant.o | ||||
| snd-hda-codec-via-objs :=	patch_via.o | ||||
|  | @ -41,6 +42,9 @@ endif | |||
| ifdef CONFIG_SND_HDA_CODEC_ATIHDMI | ||||
| obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o | ||||
| endif | ||||
| ifdef CONFIG_SND_HDA_CODEC_CIRRUS | ||||
| obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o | ||||
| endif | ||||
| ifdef CONFIG_SND_HDA_CODEC_CA0110 | ||||
| obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o | ||||
| endif | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ | |||
| #include <linux/workqueue.h> | ||||
| #include <sound/core.h> | ||||
| #include "hda_beep.h" | ||||
| #include "hda_local.h" | ||||
| 
 | ||||
| enum { | ||||
| 	DIGBEEP_HZ_STEP = 46875,	/* 46.875 Hz */ | ||||
|  | @ -118,6 +119,9 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) | |||
| 	struct hda_beep *beep; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!snd_hda_get_bool_hint(codec, "beep")) | ||||
| 		return 0; /* disabled explicitly */ | ||||
| 
 | ||||
| 	beep = kzalloc(sizeof(*beep), GFP_KERNEL); | ||||
| 	if (beep == NULL) | ||||
| 		return -ENOMEM; | ||||
|  |  | |||
|  | @ -44,6 +44,7 @@ struct hda_vendor_id { | |||
| /* codec vendor labels */ | ||||
| static struct hda_vendor_id hda_vendor_ids[] = { | ||||
| 	{ 0x1002, "ATI" }, | ||||
| 	{ 0x1013, "Cirrus Logic" }, | ||||
| 	{ 0x1057, "Motorola" }, | ||||
| 	{ 0x1095, "Silicon Image" }, | ||||
| 	{ 0x10de, "Nvidia" }, | ||||
|  | @ -150,7 +151,14 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, | |||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	val = (u32)(codec->addr & 0x0f) << 28; | ||||
| 	if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) || | ||||
| 	    (verb & ~0xfff) || (parm & ~0xffff)) { | ||||
| 		printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n", | ||||
| 		       codec->addr, direct, nid, verb, parm); | ||||
| 		return ~0; | ||||
| 	} | ||||
| 
 | ||||
| 	val = (u32)codec->addr << 28; | ||||
| 	val |= (u32)direct << 27; | ||||
| 	val |= (u32)nid << 20; | ||||
| 	val |= verb << 8; | ||||
|  | @ -167,6 +175,9 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, | |||
| 	struct hda_bus *bus = codec->bus; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (cmd == ~0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (res) | ||||
| 		*res = -1; | ||||
|  again: | ||||
|  | @ -291,11 +302,20 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, | |||
| 	unsigned int parm; | ||||
| 	int i, conn_len, conns; | ||||
| 	unsigned int shift, num_elems, mask; | ||||
| 	unsigned int wcaps; | ||||
| 	hda_nid_t prev_nid; | ||||
| 
 | ||||
| 	if (snd_BUG_ON(!conn_list || max_conns <= 0)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	wcaps = get_wcaps(codec, nid); | ||||
| 	if (!(wcaps & AC_WCAP_CONN_LIST) && | ||||
| 	    get_wcaps_type(wcaps) != AC_WID_VOL_KNB) { | ||||
| 		snd_printk(KERN_WARNING "hda_codec: " | ||||
| 			   "connection list not available for 0x%x\n", nid); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); | ||||
| 	if (parm & AC_CLIST_LONG) { | ||||
| 		/* long form */ | ||||
|  | @ -316,6 +336,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, | |||
| 		/* single connection */ | ||||
| 		parm = snd_hda_codec_read(codec, nid, 0, | ||||
| 					  AC_VERB_GET_CONNECT_LIST, 0); | ||||
| 		if (parm == -1 && codec->bus->rirb_error) | ||||
| 			return -EIO; | ||||
| 		conn_list[0] = parm & mask; | ||||
| 		return 1; | ||||
| 	} | ||||
|  | @ -327,9 +349,12 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, | |||
| 		int range_val; | ||||
| 		hda_nid_t val, n; | ||||
| 
 | ||||
| 		if (i % num_elems == 0) | ||||
| 		if (i % num_elems == 0) { | ||||
| 			parm = snd_hda_codec_read(codec, nid, 0, | ||||
| 						  AC_VERB_GET_CONNECT_LIST, i); | ||||
| 			if (parm == -1 && codec->bus->rirb_error) | ||||
| 				return -EIO; | ||||
| 		} | ||||
| 		range_val = !!(parm & (1 << (shift-1))); /* ranges */ | ||||
| 		val = parm & mask; | ||||
| 		if (val == 0) { | ||||
|  | @ -727,8 +752,7 @@ static int read_pin_defaults(struct hda_codec *codec) | |||
| 	for (i = 0; i < codec->num_nodes; i++, nid++) { | ||||
| 		struct hda_pincfg *pin; | ||||
| 		unsigned int wcaps = get_wcaps(codec, nid); | ||||
| 		unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> | ||||
| 				AC_WCAP_TYPE_SHIFT; | ||||
| 		unsigned int wid_type = get_wcaps_type(wcaps); | ||||
| 		if (wid_type != AC_WID_PIN) | ||||
| 			continue; | ||||
| 		pin = snd_array_new(&codec->init_pins); | ||||
|  | @ -891,7 +915,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, | |||
|  * Returns 0 if successful, or a negative error code. | ||||
|  */ | ||||
| int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, | ||||
| 				    int do_init, struct hda_codec **codecp) | ||||
| 				    struct hda_codec **codecp) | ||||
| { | ||||
| 	struct hda_codec *codec; | ||||
| 	char component[31]; | ||||
|  | @ -984,11 +1008,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr | |||
| 			    codec->afg ? codec->afg : codec->mfg, | ||||
| 			    AC_PWRST_D0); | ||||
| 
 | ||||
| 	if (do_init) { | ||||
| 		err = snd_hda_codec_configure(codec); | ||||
| 		if (err < 0) | ||||
| 			goto error; | ||||
| 	} | ||||
| 	snd_hda_codec_proc_new(codec); | ||||
| 
 | ||||
| 	snd_hda_create_hwdep(codec); | ||||
|  | @ -1042,6 +1061,7 @@ int snd_hda_codec_configure(struct hda_codec *codec) | |||
| 		err = init_unsol_queue(codec->bus); | ||||
| 	return err; | ||||
| } | ||||
| EXPORT_SYMBOL_HDA(snd_hda_codec_configure); | ||||
| 
 | ||||
| /**
 | ||||
|  * snd_hda_codec_setup_stream - set up the codec for streaming | ||||
|  | @ -2356,16 +2376,20 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, | |||
| 	hda_nid_t nid; | ||||
| 	int i; | ||||
| 
 | ||||
| 	snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, | ||||
| 	/* this delay seems necessary to avoid click noise at power-down */ | ||||
| 	if (power_state == AC_PWRST_D3) | ||||
| 		msleep(100); | ||||
| 	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, | ||||
| 			    power_state); | ||||
| 	msleep(10); /* partial workaround for "azx_get_response timeout" */ | ||||
| 	/* partial workaround for "azx_get_response timeout" */ | ||||
| 	if (power_state == AC_PWRST_D0) | ||||
| 		msleep(10); | ||||
| 
 | ||||
| 	nid = codec->start_nid; | ||||
| 	for (i = 0; i < codec->num_nodes; i++, nid++) { | ||||
| 		unsigned int wcaps = get_wcaps(codec, nid); | ||||
| 		if (wcaps & AC_WCAP_POWER) { | ||||
| 			unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> | ||||
| 				AC_WCAP_TYPE_SHIFT; | ||||
| 			unsigned int wid_type = get_wcaps_type(wcaps); | ||||
| 			if (power_state == AC_PWRST_D3 && | ||||
| 			    wid_type == AC_WID_PIN) { | ||||
| 				unsigned int pincap; | ||||
|  | @ -2573,7 +2597,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, | |||
| 	case 20: | ||||
| 	case 24: | ||||
| 	case 32: | ||||
| 		if (maxbps >= 32) | ||||
| 		if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) | ||||
| 			val |= 0x40; | ||||
| 		else if (maxbps >= 24) | ||||
| 			val |= 0x30; | ||||
|  | @ -2700,11 +2724,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, | |||
| 					bps = 20; | ||||
| 			} | ||||
| 		} | ||||
| 		else if (streams == AC_SUPFMT_FLOAT32) { | ||||
| 			/* should be exclusive */ | ||||
| 		if (streams & AC_SUPFMT_FLOAT32) { | ||||
| 			formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; | ||||
| 			bps = 32; | ||||
| 		} else if (streams == AC_SUPFMT_AC3) { | ||||
| 			if (!bps) | ||||
| 				bps = 32; | ||||
| 		} | ||||
| 		if (streams == AC_SUPFMT_AC3) { | ||||
| 			/* should be exclusive */ | ||||
| 			/* temporary hack: we have still no proper support
 | ||||
| 			 * for the direct AC3 stream... | ||||
|  | @ -3102,7 +3127,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, | |||
| 	tbl = q; | ||||
| 
 | ||||
| 	if (tbl->value >= 0 && tbl->value < num_configs) { | ||||
| #ifdef CONFIG_SND_DEBUG_DETECT | ||||
| #ifdef CONFIG_SND_DEBUG_VERBOSE | ||||
| 		char tmp[10]; | ||||
| 		const char *model = NULL; | ||||
| 		if (models) | ||||
|  | @ -3655,8 +3680,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, | |||
| 	end_nid = codec->start_nid + codec->num_nodes; | ||||
| 	for (nid = codec->start_nid; nid < end_nid; nid++) { | ||||
| 		unsigned int wid_caps = get_wcaps(codec, nid); | ||||
| 		unsigned int wid_type = | ||||
| 			(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; | ||||
| 		unsigned int wid_type = get_wcaps_type(wid_caps); | ||||
| 		unsigned int def_conf; | ||||
| 		short assoc, loc; | ||||
| 
 | ||||
|  |  | |||
|  | @ -830,7 +830,8 @@ enum { | |||
| int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, | ||||
| 		    struct hda_bus **busp); | ||||
| int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, | ||||
| 		      int do_init, struct hda_codec **codecp); | ||||
| 		      struct hda_codec **codecp); | ||||
| int snd_hda_codec_configure(struct hda_codec *codec); | ||||
| 
 | ||||
| /*
 | ||||
|  * low level functions | ||||
|  | @ -938,6 +939,13 @@ static inline void snd_hda_power_down(struct hda_codec *codec) {} | |||
| #define snd_hda_codec_needs_resume(codec) 1 | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_SND_HDA_PATCH_LOADER | ||||
| /*
 | ||||
|  * patch firmware | ||||
|  */ | ||||
| int snd_hda_load_patch(struct hda_bus *bus, const char *patch); | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * Codec modularization | ||||
|  */ | ||||
|  |  | |||
|  | @ -121,11 +121,17 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid | |||
| 	if (node == NULL) | ||||
| 		return -ENOMEM; | ||||
| 	node->nid = nid; | ||||
| 	nconns = snd_hda_get_connections(codec, nid, conn_list, | ||||
| 					 HDA_MAX_CONNECTIONS); | ||||
| 	if (nconns < 0) { | ||||
| 		kfree(node); | ||||
| 		return nconns; | ||||
| 	node->wid_caps = get_wcaps(codec, nid); | ||||
| 	node->type = get_wcaps_type(node->wid_caps); | ||||
| 	if (node->wid_caps & AC_WCAP_CONN_LIST) { | ||||
| 		nconns = snd_hda_get_connections(codec, nid, conn_list, | ||||
| 						 HDA_MAX_CONNECTIONS); | ||||
| 		if (nconns < 0) { | ||||
| 			kfree(node); | ||||
| 			return nconns; | ||||
| 		} | ||||
| 	} else { | ||||
| 		nconns = 0; | ||||
| 	} | ||||
| 	if (nconns <= ARRAY_SIZE(node->slist)) | ||||
| 		node->conn_list = node->slist; | ||||
|  | @ -140,8 +146,6 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid | |||
| 	} | ||||
| 	memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t)); | ||||
| 	node->nconns = nconns; | ||||
| 	node->wid_caps = get_wcaps(codec, nid); | ||||
| 	node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; | ||||
| 
 | ||||
| 	if (node->type == AC_WID_PIN) { | ||||
| 		node->pin_caps = snd_hda_query_pin_caps(codec, node->nid); | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ | |||
| #include <linux/compat.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/ctype.h> | ||||
| #include <linux/firmware.h> | ||||
| #include <sound/core.h> | ||||
| #include "hda_codec.h" | ||||
| #include "hda_local.h" | ||||
|  | @ -312,12 +313,8 @@ static ssize_t init_verbs_show(struct device *dev, | |||
| 	return len; | ||||
| } | ||||
| 
 | ||||
| static ssize_t init_verbs_store(struct device *dev, | ||||
| 				struct device_attribute *attr, | ||||
| 				const char *buf, size_t count) | ||||
| static int parse_init_verbs(struct hda_codec *codec, const char *buf) | ||||
| { | ||||
| 	struct snd_hwdep *hwdep = dev_get_drvdata(dev); | ||||
| 	struct hda_codec *codec = hwdep->private_data; | ||||
| 	struct hda_verb *v; | ||||
| 	int nid, verb, param; | ||||
| 
 | ||||
|  | @ -331,6 +328,18 @@ static ssize_t init_verbs_store(struct device *dev, | |||
| 	v->nid = nid; | ||||
| 	v->verb = verb; | ||||
| 	v->param = param; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t init_verbs_store(struct device *dev, | ||||
| 				struct device_attribute *attr, | ||||
| 				const char *buf, size_t count) | ||||
| { | ||||
| 	struct snd_hwdep *hwdep = dev_get_drvdata(dev); | ||||
| 	struct hda_codec *codec = hwdep->private_data; | ||||
| 	int err = parse_init_verbs(codec, buf); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
|  | @ -376,19 +385,15 @@ static void remove_trail_spaces(char *str) | |||
| 
 | ||||
| #define MAX_HINTS	1024 | ||||
| 
 | ||||
| static ssize_t hints_store(struct device *dev, | ||||
| 			   struct device_attribute *attr, | ||||
| 			   const char *buf, size_t count) | ||||
| static int parse_hints(struct hda_codec *codec, const char *buf) | ||||
| { | ||||
| 	struct snd_hwdep *hwdep = dev_get_drvdata(dev); | ||||
| 	struct hda_codec *codec = hwdep->private_data; | ||||
| 	char *key, *val; | ||||
| 	struct hda_hint *hint; | ||||
| 
 | ||||
| 	while (isspace(*buf)) | ||||
| 		buf++; | ||||
| 	if (!*buf || *buf == '#' || *buf == '\n') | ||||
| 		return count; | ||||
| 		return 0; | ||||
| 	if (*buf == '=') | ||||
| 		return -EINVAL; | ||||
| 	key = kstrndup_noeol(buf, 1024); | ||||
|  | @ -411,7 +416,7 @@ static ssize_t hints_store(struct device *dev, | |||
| 		kfree(hint->key); | ||||
| 		hint->key = key; | ||||
| 		hint->val = val; | ||||
| 		return count; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	/* allocate a new hint entry */ | ||||
| 	if (codec->hints.used >= MAX_HINTS) | ||||
|  | @ -424,6 +429,18 @@ static ssize_t hints_store(struct device *dev, | |||
| 	} | ||||
| 	hint->key = key; | ||||
| 	hint->val = val; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t hints_store(struct device *dev, | ||||
| 			   struct device_attribute *attr, | ||||
| 			   const char *buf, size_t count) | ||||
| { | ||||
| 	struct snd_hwdep *hwdep = dev_get_drvdata(dev); | ||||
| 	struct hda_codec *codec = hwdep->private_data; | ||||
| 	int err = parse_hints(codec, buf); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
|  | @ -469,20 +486,24 @@ static ssize_t driver_pin_configs_show(struct device *dev, | |||
| 
 | ||||
| #define MAX_PIN_CONFIGS		32 | ||||
| 
 | ||||
| static int parse_user_pin_configs(struct hda_codec *codec, const char *buf) | ||||
| { | ||||
| 	int nid, cfg; | ||||
| 
 | ||||
| 	if (sscanf(buf, "%i %i", &nid, &cfg) != 2) | ||||
| 		return -EINVAL; | ||||
| 	if (!nid) | ||||
| 		return -EINVAL; | ||||
| 	return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); | ||||
| } | ||||
| 
 | ||||
| static ssize_t user_pin_configs_store(struct device *dev, | ||||
| 				      struct device_attribute *attr, | ||||
| 				      const char *buf, size_t count) | ||||
| { | ||||
| 	struct snd_hwdep *hwdep = dev_get_drvdata(dev); | ||||
| 	struct hda_codec *codec = hwdep->private_data; | ||||
| 	int nid, cfg; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (sscanf(buf, "%i %i", &nid, &cfg) != 2) | ||||
| 		return -EINVAL; | ||||
| 	if (!nid) | ||||
| 		return -EINVAL; | ||||
| 	err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); | ||||
| 	int err = parse_user_pin_configs(codec, buf); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	return count; | ||||
|  | @ -553,3 +574,180 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) | |||
| EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint); | ||||
| 
 | ||||
| #endif /* CONFIG_SND_HDA_RECONFIG */ | ||||
| 
 | ||||
| #ifdef CONFIG_SND_HDA_PATCH_LOADER | ||||
| 
 | ||||
| /* parser mode */ | ||||
| enum { | ||||
| 	LINE_MODE_NONE, | ||||
| 	LINE_MODE_CODEC, | ||||
| 	LINE_MODE_MODEL, | ||||
| 	LINE_MODE_PINCFG, | ||||
| 	LINE_MODE_VERB, | ||||
| 	LINE_MODE_HINT, | ||||
| 	NUM_LINE_MODES, | ||||
| }; | ||||
| 
 | ||||
| static inline int strmatch(const char *a, const char *b) | ||||
| { | ||||
| 	return strnicmp(a, b, strlen(b)) == 0; | ||||
| } | ||||
| 
 | ||||
| /* parse the contents after the line "[codec]"
 | ||||
|  * accept only the line with three numbers, and assign the current codec | ||||
|  */ | ||||
| static void parse_codec_mode(char *buf, struct hda_bus *bus, | ||||
| 			     struct hda_codec **codecp) | ||||
| { | ||||
| 	unsigned int vendorid, subid, caddr; | ||||
| 	struct hda_codec *codec; | ||||
| 
 | ||||
| 	*codecp = NULL; | ||||
| 	if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { | ||||
| 		list_for_each_entry(codec, &bus->codec_list, list) { | ||||
| 			if (codec->addr == caddr) { | ||||
| 				*codecp = codec; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* parse the contents after the other command tags, [pincfg], [verb],
 | ||||
|  * [hint] and [model] | ||||
|  * just pass to the sysfs helper (only when any codec was specified) | ||||
|  */ | ||||
| static void parse_pincfg_mode(char *buf, struct hda_bus *bus, | ||||
| 			      struct hda_codec **codecp) | ||||
| { | ||||
| 	if (!*codecp) | ||||
| 		return; | ||||
| 	parse_user_pin_configs(*codecp, buf); | ||||
| } | ||||
| 
 | ||||
| static void parse_verb_mode(char *buf, struct hda_bus *bus, | ||||
| 			    struct hda_codec **codecp) | ||||
| { | ||||
| 	if (!*codecp) | ||||
| 		return; | ||||
| 	parse_init_verbs(*codecp, buf); | ||||
| } | ||||
| 
 | ||||
| static void parse_hint_mode(char *buf, struct hda_bus *bus, | ||||
| 			    struct hda_codec **codecp) | ||||
| { | ||||
| 	if (!*codecp) | ||||
| 		return; | ||||
| 	parse_hints(*codecp, buf); | ||||
| } | ||||
| 
 | ||||
| static void parse_model_mode(char *buf, struct hda_bus *bus, | ||||
| 			     struct hda_codec **codecp) | ||||
| { | ||||
| 	if (!*codecp) | ||||
| 		return; | ||||
| 	kfree((*codecp)->modelname); | ||||
| 	(*codecp)->modelname = kstrdup(buf, GFP_KERNEL); | ||||
| } | ||||
| 
 | ||||
| struct hda_patch_item { | ||||
| 	const char *tag; | ||||
| 	void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc); | ||||
| }; | ||||
| 
 | ||||
| static struct hda_patch_item patch_items[NUM_LINE_MODES] = { | ||||
| 	[LINE_MODE_CODEC] = { "[codec]", parse_codec_mode }, | ||||
| 	[LINE_MODE_MODEL] = { "[model]", parse_model_mode }, | ||||
| 	[LINE_MODE_VERB] = { "[verb]", parse_verb_mode }, | ||||
| 	[LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode }, | ||||
| 	[LINE_MODE_HINT] = { "[hint]", parse_hint_mode }, | ||||
| }; | ||||
| 
 | ||||
| /* check the line starting with '[' -- change the parser mode accodingly */ | ||||
| static int parse_line_mode(char *buf, struct hda_bus *bus) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 0; i < ARRAY_SIZE(patch_items); i++) { | ||||
| 		if (!patch_items[i].tag) | ||||
| 			continue; | ||||
| 		if (strmatch(buf, patch_items[i].tag)) | ||||
| 			return i; | ||||
| 	} | ||||
| 	return LINE_MODE_NONE; | ||||
| } | ||||
| 
 | ||||
| /* copy one line from the buffer in fw, and update the fields in fw
 | ||||
|  * return zero if it reaches to the end of the buffer, or non-zero | ||||
|  * if successfully copied a line | ||||
|  * | ||||
|  * the spaces at the beginning and the end of the line are stripped | ||||
|  */ | ||||
| static int get_line_from_fw(char *buf, int size, struct firmware *fw) | ||||
| { | ||||
| 	int len; | ||||
| 	const char *p = fw->data; | ||||
| 	while (isspace(*p) && fw->size) { | ||||
| 		p++; | ||||
| 		fw->size--; | ||||
| 	} | ||||
| 	if (!fw->size) | ||||
| 		return 0; | ||||
| 	if (size < fw->size) | ||||
| 		size = fw->size; | ||||
| 
 | ||||
| 	for (len = 0; len < fw->size; len++) { | ||||
| 		if (!*p) | ||||
| 			break; | ||||
| 		if (*p == '\n') { | ||||
| 			p++; | ||||
| 			len++; | ||||
| 			break; | ||||
| 		} | ||||
| 		if (len < size) | ||||
| 			*buf++ = *p++; | ||||
| 	} | ||||
| 	*buf = 0; | ||||
| 	fw->size -= len; | ||||
| 	fw->data = p; | ||||
| 	remove_trail_spaces(buf); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * load a "patch" firmware file and parse it | ||||
|  */ | ||||
| int snd_hda_load_patch(struct hda_bus *bus, const char *patch) | ||||
| { | ||||
| 	int err; | ||||
| 	const struct firmware *fw; | ||||
| 	struct firmware tmp; | ||||
| 	char buf[128]; | ||||
| 	struct hda_codec *codec; | ||||
| 	int line_mode; | ||||
| 	struct device *dev = bus->card->dev; | ||||
| 
 | ||||
| 	if (snd_BUG_ON(!dev)) | ||||
| 		return -ENODEV; | ||||
| 	err = request_firmware(&fw, patch, dev); | ||||
| 	if (err < 0) { | ||||
| 		printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n", | ||||
| 		       patch); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	tmp = *fw; | ||||
| 	line_mode = LINE_MODE_NONE; | ||||
| 	codec = NULL; | ||||
| 	while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) { | ||||
| 		if (!*buf || *buf == '#' || *buf == '\n') | ||||
| 			continue; | ||||
| 		if (*buf == '[') | ||||
| 			line_mode = parse_line_mode(buf, bus); | ||||
| 		else if (patch_items[line_mode].parser) | ||||
| 			patch_items[line_mode].parser(buf, bus, &codec); | ||||
| 	} | ||||
| 	release_firmware(fw); | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_HDA(snd_hda_load_patch); | ||||
| #endif /* CONFIG_SND_HDA_PATCH_LOADER */ | ||||
|  |  | |||
|  | @ -61,6 +61,9 @@ static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; | |||
| static int probe_only[SNDRV_CARDS]; | ||||
| static int single_cmd; | ||||
| static int enable_msi; | ||||
| #ifdef CONFIG_SND_HDA_PATCH_LOADER | ||||
| static char *patch[SNDRV_CARDS]; | ||||
| #endif | ||||
| 
 | ||||
| module_param_array(index, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); | ||||
|  | @ -84,6 +87,10 @@ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " | |||
| 		 "(for debugging only)."); | ||||
| module_param(enable_msi, int, 0444); | ||||
| MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); | ||||
| #ifdef CONFIG_SND_HDA_PATCH_LOADER | ||||
| module_param_array(patch, charp, NULL, 0444); | ||||
| MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface."); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_SND_HDA_POWER_SAVE | ||||
| static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; | ||||
|  | @ -1331,8 +1338,7 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = { | |||
| 	[AZX_DRIVER_TERA] = 1, | ||||
| }; | ||||
| 
 | ||||
| static int __devinit azx_codec_create(struct azx *chip, const char *model, | ||||
| 				      int no_init) | ||||
| static int __devinit azx_codec_create(struct azx *chip, const char *model) | ||||
| { | ||||
| 	struct hda_bus_template bus_temp; | ||||
| 	int c, codecs, err; | ||||
|  | @ -1391,7 +1397,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, | |||
| 	for (c = 0; c < max_slots; c++) { | ||||
| 		if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { | ||||
| 			struct hda_codec *codec; | ||||
| 			err = snd_hda_codec_new(chip->bus, c, !no_init, &codec); | ||||
| 			err = snd_hda_codec_new(chip->bus, c, &codec); | ||||
| 			if (err < 0) | ||||
| 				continue; | ||||
| 			codecs++; | ||||
|  | @ -1401,7 +1407,16 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, | |||
| 		snd_printk(KERN_ERR SFX "no codecs initialized\n"); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* configure each codec instance */ | ||||
| static int __devinit azx_codec_configure(struct azx *chip) | ||||
| { | ||||
| 	struct hda_codec *codec; | ||||
| 	list_for_each_entry(codec, &chip->bus->codec_list, list) { | ||||
| 		snd_hda_codec_configure(codec); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -2284,6 +2299,30 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * white-list for enable_msi | ||||
|  */ | ||||
| static struct snd_pci_quirk msi_white_list[] __devinitdata = { | ||||
| 	SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1), | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static void __devinit check_msi(struct azx *chip) | ||||
| { | ||||
| 	const struct snd_pci_quirk *q; | ||||
| 
 | ||||
| 	chip->msi = enable_msi; | ||||
| 	if (chip->msi) | ||||
| 		return; | ||||
| 	q = snd_pci_quirk_lookup(chip->pci, msi_white_list); | ||||
| 	if (q) { | ||||
| 		printk(KERN_INFO | ||||
| 		       "hda_intel: msi for device %04x:%04x set to %d\n", | ||||
| 		       q->subvendor, q->subdevice, q->value); | ||||
| 		chip->msi = q->value; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * constructor | ||||
|  | @ -2318,7 +2357,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, | |||
| 	chip->pci = pci; | ||||
| 	chip->irq = -1; | ||||
| 	chip->driver_type = driver_type; | ||||
| 	chip->msi = enable_msi; | ||||
| 	check_msi(chip); | ||||
| 	chip->dev_index = dev; | ||||
| 	INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work); | ||||
| 
 | ||||
|  | @ -2526,15 +2565,32 @@ static int __devinit azx_probe(struct pci_dev *pci, | |||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	/* set this here since it's referred in snd_hda_load_patch() */ | ||||
| 	snd_card_set_dev(card, &pci->dev); | ||||
| 
 | ||||
| 	err = azx_create(card, pci, dev, pci_id->driver_data, &chip); | ||||
| 	if (err < 0) | ||||
| 		goto out_free; | ||||
| 	card->private_data = chip; | ||||
| 
 | ||||
| 	/* create codec instances */ | ||||
| 	err = azx_codec_create(chip, model[dev], probe_only[dev]); | ||||
| 	err = azx_codec_create(chip, model[dev]); | ||||
| 	if (err < 0) | ||||
| 		goto out_free; | ||||
| #ifdef CONFIG_SND_HDA_PATCH_LOADER | ||||
| 	if (patch[dev]) { | ||||
| 		snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n", | ||||
| 			   patch[dev]); | ||||
| 		err = snd_hda_load_patch(chip->bus, patch[dev]); | ||||
| 		if (err < 0) | ||||
| 			goto out_free; | ||||
| 	} | ||||
| #endif | ||||
| 	if (!probe_only[dev]) { | ||||
| 		err = azx_codec_configure(chip); | ||||
| 		if (err < 0) | ||||
| 			goto out_free; | ||||
| 	} | ||||
| 
 | ||||
| 	/* create PCM streams */ | ||||
| 	err = snd_hda_build_pcms(chip->bus); | ||||
|  | @ -2546,8 +2602,6 @@ static int __devinit azx_probe(struct pci_dev *pci, | |||
| 	if (err < 0) | ||||
| 		goto out_free; | ||||
| 
 | ||||
| 	snd_card_set_dev(card, &pci->dev); | ||||
| 
 | ||||
| 	err = snd_card_register(card); | ||||
| 	if (err < 0) | ||||
| 		goto out_free; | ||||
|  | @ -2649,11 +2703,15 @@ static struct pci_device_id azx_ids[] = { | |||
| 	/* this entry seems still valid -- i.e. without emu20kx chip */ | ||||
| 	{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC }, | ||||
| #endif | ||||
| 	/* AMD Generic, PCI class code and Vendor ID for HD Audio */ | ||||
| 	/* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */ | ||||
| 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), | ||||
| 	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, | ||||
| 	  .class_mask = 0xffffff, | ||||
| 	  .driver_data = AZX_DRIVER_GENERIC }, | ||||
| 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID), | ||||
| 	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, | ||||
| 	  .class_mask = 0xffffff, | ||||
| 	  .driver_data = AZX_DRIVER_GENERIC }, | ||||
| 	{ 0, } | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(pci, azx_ids); | ||||
|  |  | |||
|  | @ -99,7 +99,6 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, | |||
| int snd_hda_add_vmaster(struct hda_codec *codec, char *name, | ||||
| 			unsigned int *tlv, const char **slaves); | ||||
| int snd_hda_codec_reset(struct hda_codec *codec); | ||||
| int snd_hda_codec_configure(struct hda_codec *codec); | ||||
| 
 | ||||
| /* amp value bits */ | ||||
| #define HDA_AMP_MUTE	0x80 | ||||
|  | @ -408,6 +407,19 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) | |||
| 	return codec->wcaps[nid - codec->start_nid]; | ||||
| } | ||||
| 
 | ||||
| /* get the widget type from widget capability bits */ | ||||
| #define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT) | ||||
| 
 | ||||
| static inline unsigned int get_wcaps_channels(u32 wcaps) | ||||
| { | ||||
| 	unsigned int chans; | ||||
| 
 | ||||
| 	chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; | ||||
| 	chans = ((chans << 1) | 1) + 1; | ||||
| 
 | ||||
| 	return chans; | ||||
| } | ||||
| 
 | ||||
| u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); | ||||
| int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, | ||||
| 			      unsigned int caps); | ||||
|  |  | |||
|  | @ -508,17 +508,14 @@ static void print_codec_info(struct snd_info_entry *entry, | |||
| 		unsigned int wid_caps = | ||||
| 			snd_hda_param_read(codec, nid, | ||||
| 					   AC_PAR_AUDIO_WIDGET_CAP); | ||||
| 		unsigned int wid_type = | ||||
| 			(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; | ||||
| 		unsigned int wid_type = get_wcaps_type(wid_caps); | ||||
| 		hda_nid_t conn[HDA_MAX_CONNECTIONS]; | ||||
| 		int conn_len = 0; | ||||
| 
 | ||||
| 		snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid, | ||||
| 			    get_wid_type_name(wid_type), wid_caps); | ||||
| 		if (wid_caps & AC_WCAP_STEREO) { | ||||
| 			unsigned int chans; | ||||
| 			chans = (wid_caps & AC_WCAP_CHAN_CNT_EXT) >> 13; | ||||
| 			chans = ((chans << 1) | 1) + 1; | ||||
| 			unsigned int chans = get_wcaps_channels(wid_caps); | ||||
| 			if (chans == 2) | ||||
| 				snd_iprintf(buffer, " Stereo"); | ||||
| 			else | ||||
|  |  | |||
|  | @ -2982,7 +2982,8 @@ static int patch_ad1988(struct hda_codec *codec) | |||
| 	board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, | ||||
| 						  ad1988_models, ad1988_cfg_tbl); | ||||
| 	if (board_config < 0) { | ||||
| 		printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n"); | ||||
| 		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", | ||||
| 		       codec->chip_name); | ||||
| 		board_config = AD1988_AUTO; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -3702,19 +3703,29 @@ static struct hda_amp_list ad1884a_loopbacks[] = { | |||
|  * Port F: Internal speakers | ||||
|  */ | ||||
| 
 | ||||
| static struct hda_input_mux ad1884a_laptop_capture_source = { | ||||
| 	.num_items = 4, | ||||
| 	.items = { | ||||
| 		{ "Mic", 0x0 },		/* port-B */ | ||||
| 		{ "Internal Mic", 0x1 }, /* port-C */ | ||||
| 		{ "Dock Mic", 0x4 },	/* port-E */ | ||||
| 		{ "Mix", 0x3 }, | ||||
| 	}, | ||||
| }; | ||||
| static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, | ||||
| 					struct snd_ctl_elem_value *ucontrol) | ||||
| { | ||||
| 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||||
| 	int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); | ||||
| 	int mute = (!ucontrol->value.integer.value[0] && | ||||
| 		    !ucontrol->value.integer.value[1]); | ||||
| 	/* toggle GPIO1 according to the mute state */ | ||||
| 	snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, | ||||
| 			    mute ? 0x02 : 0x0); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { | ||||
| 	HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), | ||||
| 	HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), | ||||
| 	{ | ||||
| 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||||
| 		.name = "Master Playback Switch", | ||||
| 		.info = snd_hda_mixer_amp_switch_info, | ||||
| 		.get = snd_hda_mixer_amp_switch_get, | ||||
| 		.put = ad1884a_mobile_master_sw_put, | ||||
| 		.private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), | ||||
| 	}, | ||||
| 	HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), | ||||
| 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), | ||||
| 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), | ||||
|  | @ -3729,36 +3740,9 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { | |||
| 	HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), | ||||
| 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), | ||||
| 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), | ||||
| 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), | ||||
| 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), | ||||
| 	{ | ||||
| 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||||
| 		/* The multiple "Capture Source" controls confuse alsamixer
 | ||||
| 		 * So call somewhat different.. | ||||
| 		 */ | ||||
| 		/* .name = "Capture Source", */ | ||||
| 		.name = "Input Source", | ||||
| 		.count = 2, | ||||
| 		.info = ad198x_mux_enum_info, | ||||
| 		.get = ad198x_mux_enum_get, | ||||
| 		.put = ad198x_mux_enum_put, | ||||
| 	}, | ||||
| 	{ } /* end */ | ||||
| }; | ||||
| 
 | ||||
| static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, | ||||
| 				        struct snd_ctl_elem_value *ucontrol) | ||||
| { | ||||
| 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||||
| 	int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); | ||||
| 	int mute = (!ucontrol->value.integer.value[0] && | ||||
| 		    !ucontrol->value.integer.value[1]); | ||||
| 	/* toggle GPIO1 according to the mute state */ | ||||
| 	snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, | ||||
| 			    mute ? 0x02 : 0x0); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { | ||||
| 	HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), | ||||
| 	/*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ | ||||
|  | @ -3828,6 +3812,63 @@ static int ad1884a_hp_init(struct hda_codec *codec) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* mute internal speaker if HP or docking HP is plugged */ | ||||
| static void ad1884a_laptop_automute(struct hda_codec *codec) | ||||
| { | ||||
| 	unsigned int present; | ||||
| 
 | ||||
| 	present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0); | ||||
| 	present &= AC_PINSENSE_PRESENCE; | ||||
| 	if (!present) { | ||||
| 		present = snd_hda_codec_read(codec, 0x12, 0, | ||||
| 					     AC_VERB_GET_PIN_SENSE, 0); | ||||
| 		present &= AC_PINSENSE_PRESENCE; | ||||
| 	} | ||||
| 	snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, | ||||
| 				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); | ||||
| 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, | ||||
| 			    present ? 0x00 : 0x02); | ||||
| } | ||||
| 
 | ||||
| /* switch to external mic if plugged */ | ||||
| static void ad1884a_laptop_automic(struct hda_codec *codec) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 
 | ||||
| 	if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & | ||||
| 	    AC_PINSENSE_PRESENCE) | ||||
| 		idx = 0; | ||||
| 	else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & | ||||
| 		 AC_PINSENSE_PRESENCE) | ||||
| 		idx = 4; | ||||
| 	else | ||||
| 		idx = 1; | ||||
| 	snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx); | ||||
| } | ||||
| 
 | ||||
| /* unsolicited event for HP jack sensing */ | ||||
| static void ad1884a_laptop_unsol_event(struct hda_codec *codec, | ||||
| 				       unsigned int res) | ||||
| { | ||||
| 	switch (res >> 26) { | ||||
| 	case AD1884A_HP_EVENT: | ||||
| 		ad1884a_laptop_automute(codec); | ||||
| 		break; | ||||
| 	case AD1884A_MIC_EVENT: | ||||
| 		ad1884a_laptop_automic(codec); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* initialize jack-sensing, too */ | ||||
| static int ad1884a_laptop_init(struct hda_codec *codec) | ||||
| { | ||||
| 	ad198x_init(codec); | ||||
| 	ad1884a_laptop_automute(codec); | ||||
| 	ad1884a_laptop_automic(codec); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* additional verbs for laptop model */ | ||||
| static struct hda_verb ad1884a_laptop_verbs[] = { | ||||
| 	/* Port-A (HP) pin - always unmuted */ | ||||
|  | @ -3844,11 +3885,19 @@ static struct hda_verb ad1884a_laptop_verbs[] = { | |||
| 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ | ||||
| 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ | ||||
| 	/* Port-D (docking line-out) pin - default unmuted */ | ||||
| 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||||
| 	/* analog mix */ | ||||
| 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, | ||||
| 	/* unsolicited event for pin-sense */ | ||||
| 	{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, | ||||
| 	{0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, | ||||
| 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, | ||||
| 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, | ||||
| 	/* allow to touch GPIO1 (for mute control) */ | ||||
| 	{0x01, AC_VERB_SET_GPIO_MASK, 0x02}, | ||||
| 	{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, | ||||
| 	{0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ | ||||
| 	{ } /* end */ | ||||
| }; | ||||
| 
 | ||||
|  | @ -4008,6 +4057,7 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = { | |||
| 	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP), | ||||
| 	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP), | ||||
| 	SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP), | ||||
| 	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE), | ||||
| 	SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), | ||||
| 	{} | ||||
| }; | ||||
|  | @ -4057,9 +4107,8 @@ static int patch_ad1884a(struct hda_codec *codec) | |||
| 		spec->mixers[0] = ad1884a_laptop_mixers; | ||||
| 		spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; | ||||
| 		spec->multiout.dig_out_nid = 0; | ||||
| 		spec->input_mux = &ad1884a_laptop_capture_source; | ||||
| 		codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; | ||||
| 		codec->patch_ops.init = ad1884a_hp_init; | ||||
| 		codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event; | ||||
| 		codec->patch_ops.init = ad1884a_laptop_init; | ||||
| 		/* set the upper-limit for mixer amp to 0dB for avoiding the
 | ||||
| 		 * possible damage by overloading | ||||
| 		 */ | ||||
|  |  | |||
|  | @ -141,8 +141,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec) | |||
| 	/* FIXME: we must check ELD and change the PCM parameters dynamically
 | ||||
| 	 */ | ||||
| 	chans = get_wcaps(codec, CVT_NID); | ||||
| 	chans = (chans & AC_WCAP_CHAN_CNT_EXT) >> 13; | ||||
| 	chans = ((chans << 1) | 1) + 1; | ||||
| 	chans = get_wcaps_channels(chans); | ||||
| 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -459,8 +459,7 @@ static void parse_input(struct hda_codec *codec) | |||
| 	nid = codec->start_nid; | ||||
| 	for (i = 0; i < codec->num_nodes; i++, nid++) { | ||||
| 		unsigned int wcaps = get_wcaps(codec, nid); | ||||
| 		unsigned int type = (wcaps & AC_WCAP_TYPE) >> | ||||
| 			AC_WCAP_TYPE_SHIFT; | ||||
| 		unsigned int type = get_wcaps_type(wcaps); | ||||
| 		if (type != AC_WID_AUD_IN) | ||||
| 			continue; | ||||
| 		if (snd_hda_get_connections(codec, nid, &pin, 1) != 1) | ||||
|  |  | |||
							
								
								
									
										1194
									
								
								sound/pci/hda/patch_cirrus.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1194
									
								
								sound/pci/hda/patch_cirrus.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -635,7 +635,8 @@ static int patch_cmi9880(struct hda_codec *codec) | |||
| 							cmi9880_models, | ||||
| 							cmi9880_cfg_tbl); | ||||
| 	if (spec->board_config < 0) { | ||||
| 		snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n"); | ||||
| 		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", | ||||
| 			    codec->chip_name); | ||||
| 		spec->board_config = CMI_AUTO; /* try everything */ | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -108,6 +108,8 @@ struct conexant_spec { | |||
| 	struct hda_input_mux private_imux; | ||||
| 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; | ||||
| 
 | ||||
| 	unsigned int dell_automute; | ||||
| 	unsigned int port_d_mode; | ||||
| }; | ||||
| 
 | ||||
| static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, | ||||
|  | @ -1908,6 +1910,480 @@ static int patch_cxt5051(struct hda_codec *codec) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Conexant 5066 specific */ | ||||
| 
 | ||||
| static hda_nid_t cxt5066_dac_nids[1] = { 0x10 }; | ||||
| static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 }; | ||||
| static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 }; | ||||
| #define CXT5066_SPDIF_OUT	0x21 | ||||
| 
 | ||||
| static struct hda_channel_mode cxt5066_modes[1] = { | ||||
| 	{ 2, NULL }, | ||||
| }; | ||||
| 
 | ||||
| static void cxt5066_update_speaker(struct hda_codec *codec) | ||||
| { | ||||
| 	struct conexant_spec *spec = codec->spec; | ||||
| 	unsigned int pinctl; | ||||
| 
 | ||||
| 	snd_printdd("CXT5066: update speaker, hp_present=%d\n", | ||||
| 		spec->hp_present); | ||||
| 
 | ||||
| 	/* Port A (HP) */ | ||||
| 	pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0; | ||||
| 	snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||||
| 			pinctl); | ||||
| 
 | ||||
| 	/* Port D (HP/LO) */ | ||||
| 	pinctl = ((spec->hp_present & 2) && spec->cur_eapd) | ||||
| 		? spec->port_d_mode : 0; | ||||
| 	snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||||
| 			pinctl); | ||||
| 
 | ||||
| 	/* CLASS_D AMP */ | ||||
| 	pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; | ||||
| 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||||
| 			pinctl); | ||||
| 
 | ||||
| 	if (spec->dell_automute) { | ||||
| 		/* DELL AIO Port Rule: PortA > PortD > IntSpk */ | ||||
| 		pinctl = (!(spec->hp_present & 1) && spec->cur_eapd) | ||||
| 			? PIN_OUT : 0; | ||||
| 		snd_hda_codec_write(codec, 0x1c, 0, | ||||
| 			AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* turn on/off EAPD (+ mute HP) as a master switch */ | ||||
| static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, | ||||
| 				    struct snd_ctl_elem_value *ucontrol) | ||||
| { | ||||
| 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||||
| 
 | ||||
| 	if (!cxt_eapd_put(kcontrol, ucontrol)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	cxt5066_update_speaker(codec); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| /* toggle input of built-in and mic jack appropriately */ | ||||
| static void cxt5066_automic(struct hda_codec *codec) | ||||
| { | ||||
| 	static struct hda_verb ext_mic_present[] = { | ||||
| 		/* enable external mic, port B */ | ||||
| 		{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||||
| 
 | ||||
| 		/* switch to external mic input */ | ||||
| 		{0x17, AC_VERB_SET_CONNECT_SEL, 0}, | ||||
| 
 | ||||
| 		/* disable internal mic, port C */ | ||||
| 		{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||||
| 		{} | ||||
| 	}; | ||||
| 	static struct hda_verb ext_mic_absent[] = { | ||||
| 		/* enable internal mic, port C */ | ||||
| 		{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||||
| 
 | ||||
| 		/* switch to internal mic input */ | ||||
| 		{0x17, AC_VERB_SET_CONNECT_SEL, 1}, | ||||
| 
 | ||||
| 		/* disable external mic, port B */ | ||||
| 		{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||||
| 		{} | ||||
| 	}; | ||||
| 	unsigned int present; | ||||
| 
 | ||||
| 	present = snd_hda_codec_read(codec, 0x1a, 0, | ||||
| 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||||
| 	if (present) { | ||||
| 		snd_printdd("CXT5066: external microphone detected\n"); | ||||
| 		snd_hda_sequence_write(codec, ext_mic_present); | ||||
| 	} else { | ||||
| 		snd_printdd("CXT5066: external microphone absent\n"); | ||||
| 		snd_hda_sequence_write(codec, ext_mic_absent); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* mute internal speaker if HP is plugged */ | ||||
| static void cxt5066_hp_automute(struct hda_codec *codec) | ||||
| { | ||||
| 	struct conexant_spec *spec = codec->spec; | ||||
| 	unsigned int portA, portD; | ||||
| 
 | ||||
| 	/* Port A */ | ||||
| 	portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0) | ||||
| 		& AC_PINSENSE_PRESENCE; | ||||
| 
 | ||||
| 	/* Port D */ | ||||
| 	portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) | ||||
| 		& AC_PINSENSE_PRESENCE) << 1; | ||||
| 
 | ||||
| 	spec->hp_present = !!(portA | portD); | ||||
| 	snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", | ||||
| 		portA, portD, spec->hp_present); | ||||
| 	cxt5066_update_speaker(codec); | ||||
| } | ||||
| 
 | ||||
| /* unsolicited event for jack sensing */ | ||||
| static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) | ||||
| { | ||||
| 	snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); | ||||
| 	switch (res >> 26) { | ||||
| 	case CONEXANT_HP_EVENT: | ||||
| 		cxt5066_hp_automute(codec); | ||||
| 		break; | ||||
| 	case CONEXANT_MIC_EVENT: | ||||
| 		cxt5066_automic(codec); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct hda_input_mux cxt5066_analog_mic_boost = { | ||||
| 	.num_items = 5, | ||||
| 	.items = { | ||||
| 		{ "0dB",  0 }, | ||||
| 		{ "10dB", 1 }, | ||||
| 		{ "20dB", 2 }, | ||||
| 		{ "30dB", 3 }, | ||||
| 		{ "40dB", 4 }, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, | ||||
| 					   struct snd_ctl_elem_info *uinfo) | ||||
| { | ||||
| 	return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo); | ||||
| } | ||||
| 
 | ||||
| static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol, | ||||
| 					  struct snd_ctl_elem_value *ucontrol) | ||||
| { | ||||
| 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||||
| 	int val; | ||||
| 
 | ||||
| 	val = snd_hda_codec_read(codec, 0x17, 0, | ||||
| 		AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_OUTPUT); | ||||
| 
 | ||||
| 	ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, | ||||
| 					  struct snd_ctl_elem_value *ucontrol) | ||||
| { | ||||
| 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||||
| 	const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; | ||||
| 	unsigned int idx; | ||||
| 
 | ||||
| 	if (!imux->num_items) | ||||
| 		return 0; | ||||
| 	idx = ucontrol->value.enumerated.item[0]; | ||||
| 	if (idx >= imux->num_items) | ||||
| 		idx = imux->num_items - 1; | ||||
| 
 | ||||
| 	snd_hda_codec_write_cache(codec, 0x17, 0, | ||||
| 		AC_VERB_SET_AMP_GAIN_MUTE, | ||||
| 		AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | | ||||
| 			imux->items[idx].index); | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static struct hda_input_mux cxt5066_capture_source = { | ||||
| 	.num_items = 4, | ||||
| 	.items = { | ||||
| 		{ "Mic B", 0 }, | ||||
| 		{ "Mic C", 1 }, | ||||
| 		{ "Mic E", 2 }, | ||||
| 		{ "Mic F", 3 }, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static struct hda_bind_ctls cxt5066_bind_capture_vol_others = { | ||||
| 	.ops = &snd_hda_bind_vol, | ||||
| 	.values = { | ||||
| 		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT), | ||||
| 		HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT), | ||||
| 		0 | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static struct hda_bind_ctls cxt5066_bind_capture_sw_others = { | ||||
| 	.ops = &snd_hda_bind_sw, | ||||
| 	.values = { | ||||
| 		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT), | ||||
| 		HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT), | ||||
| 		0 | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static struct snd_kcontrol_new cxt5066_mixer_master[] = { | ||||
| 	HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { | ||||
| 	{ | ||||
| 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||||
| 		.name = "Master Playback Volume", | ||||
| 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||||
| 				  SNDRV_CTL_ELEM_ACCESS_TLV_READ | | ||||
| 				  SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, | ||||
| 		.info = snd_hda_mixer_amp_volume_info, | ||||
| 		.get = snd_hda_mixer_amp_volume_get, | ||||
| 		.put = snd_hda_mixer_amp_volume_put, | ||||
| 		.tlv = { .c = snd_hda_mixer_amp_tlv }, | ||||
| 		/* offset by 28 volume steps to limit minimum gain to -46dB */ | ||||
| 		.private_value = | ||||
| 			HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28), | ||||
| 	}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static struct snd_kcontrol_new cxt5066_mixers[] = { | ||||
| 	{ | ||||
| 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||||
| 		.name = "Master Playback Switch", | ||||
| 		.info = cxt_eapd_info, | ||||
| 		.get = cxt_eapd_get, | ||||
| 		.put = cxt5066_hp_master_sw_put, | ||||
| 		.private_value = 0x1d, | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||||
| 		.name = "Analog Mic Boost Capture Enum", | ||||
| 		.info = cxt5066_mic_boost_mux_enum_info, | ||||
| 		.get = cxt5066_mic_boost_mux_enum_get, | ||||
| 		.put = cxt5066_mic_boost_mux_enum_put, | ||||
| 	}, | ||||
| 
 | ||||
| 	HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), | ||||
| 	HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others), | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static struct hda_verb cxt5066_init_verbs[] = { | ||||
| 	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */ | ||||
| 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */ | ||||
| 	{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */ | ||||
| 	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */ | ||||
| 
 | ||||
| 	/* Speakers  */ | ||||
| 	{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||||
| 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||||
| 
 | ||||
| 	/* HP, Amp  */ | ||||
| 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||||
| 	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||||
| 
 | ||||
| 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||||
| 	{0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||||
| 
 | ||||
| 	/* DAC1 */ | ||||
| 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||||
| 
 | ||||
| 	/* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */ | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50}, | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, | ||||
| 
 | ||||
| 	/* no digital microphone support yet */ | ||||
| 	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||||
| 
 | ||||
| 	/* Audio input selector */ | ||||
| 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, | ||||
| 
 | ||||
| 	/* SPDIF route: PCM */ | ||||
| 	{0x20, AC_VERB_SET_CONNECT_SEL, 0x0}, | ||||
| 	{0x22, AC_VERB_SET_CONNECT_SEL, 0x0}, | ||||
| 
 | ||||
| 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||||
| 	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||||
| 
 | ||||
| 	/* EAPD */ | ||||
| 	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ | ||||
| 
 | ||||
| 	/* not handling these yet */ | ||||
| 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||||
| 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||||
| 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||||
| 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||||
| 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||||
| 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||||
| 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||||
| 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, | ||||
| 	{ } /* end */ | ||||
| }; | ||||
| 
 | ||||
| static struct hda_verb cxt5066_init_verbs_olpc[] = { | ||||
| 	/* Port A: headphones */ | ||||
| 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||||
| 	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||||
| 
 | ||||
| 	/* Port B: external microphone */ | ||||
| 	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||||
| 
 | ||||
| 	/* Port C: internal microphone */ | ||||
| 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||||
| 
 | ||||
| 	/* Port D: unused */ | ||||
| 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||||
| 
 | ||||
| 	/* Port E: unused, but has primary EAPD */ | ||||
| 	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||||
| 	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ | ||||
| 
 | ||||
| 	/* Port F: unused */ | ||||
| 	{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||||
| 
 | ||||
| 	/* Port G: internal speakers */ | ||||
| 	{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||||
| 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ | ||||
| 
 | ||||
| 	/* DAC1 */ | ||||
| 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||||
| 
 | ||||
| 	/* DAC2: unused */ | ||||
| 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||||
| 
 | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, | ||||
| 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||||
| 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||||
| 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||||
| 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, | ||||
| 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||||
| 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||||
| 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||||
| 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, | ||||
| 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||||
| 
 | ||||
| 	/* Disable digital microphone port */ | ||||
| 	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||||
| 
 | ||||
| 	/* Audio input selectors */ | ||||
| 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, | ||||
| 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, | ||||
| 
 | ||||
| 	/* Disable SPDIF */ | ||||
| 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||||
| 	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, | ||||
| 
 | ||||
| 	/* enable unsolicited events for Port A and B */ | ||||
| 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, | ||||
| 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, | ||||
| 	{ } /* end */ | ||||
| }; | ||||
| 
 | ||||
| static struct hda_verb cxt5066_init_verbs_portd_lo[] = { | ||||
| 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||||
| 	{ } /* end */ | ||||
| }; | ||||
| 
 | ||||
| /* initialize jack-sensing, too */ | ||||
| static int cxt5066_init(struct hda_codec *codec) | ||||
| { | ||||
| 	snd_printdd("CXT5066: init\n"); | ||||
| 	conexant_init(codec); | ||||
| 	if (codec->patch_ops.unsol_event) { | ||||
| 		cxt5066_hp_automute(codec); | ||||
| 		cxt5066_automic(codec); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| enum { | ||||
| 	CXT5066_LAPTOP,			/* Laptops w/ EAPD support */ | ||||
| 	CXT5066_DELL_LAPTOP,	/* Dell Laptop */ | ||||
| 	CXT5066_OLPC_XO_1_5,	/* OLPC XO 1.5 */ | ||||
| 	CXT5066_MODELS | ||||
| }; | ||||
| 
 | ||||
| static const char *cxt5066_models[CXT5066_MODELS] = { | ||||
| 	[CXT5066_LAPTOP]		= "laptop", | ||||
| 	[CXT5066_DELL_LAPTOP]	= "dell-laptop", | ||||
| 	[CXT5066_OLPC_XO_1_5]	= "olpc-xo-1_5", | ||||
| }; | ||||
| 
 | ||||
| static struct snd_pci_quirk cxt5066_cfg_tbl[] = { | ||||
| 	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", | ||||
| 		      CXT5066_LAPTOP), | ||||
| 	SND_PCI_QUIRK(0x1028, 0x02f5, "Dell", | ||||
| 		      CXT5066_DELL_LAPTOP), | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static int patch_cxt5066(struct hda_codec *codec) | ||||
| { | ||||
| 	struct conexant_spec *spec; | ||||
| 	int board_config; | ||||
| 
 | ||||
| 	spec = kzalloc(sizeof(*spec), GFP_KERNEL); | ||||
| 	if (!spec) | ||||
| 		return -ENOMEM; | ||||
| 	codec->spec = spec; | ||||
| 
 | ||||
| 	codec->patch_ops = conexant_patch_ops; | ||||
| 	codec->patch_ops.init = cxt5066_init; | ||||
| 
 | ||||
| 	spec->dell_automute = 0; | ||||
| 	spec->multiout.max_channels = 2; | ||||
| 	spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids); | ||||
| 	spec->multiout.dac_nids = cxt5066_dac_nids; | ||||
| 	spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT; | ||||
| 	spec->num_adc_nids = 1; | ||||
| 	spec->adc_nids = cxt5066_adc_nids; | ||||
| 	spec->capsrc_nids = cxt5066_capsrc_nids; | ||||
| 	spec->input_mux = &cxt5066_capture_source; | ||||
| 
 | ||||
| 	spec->port_d_mode = PIN_HP; | ||||
| 
 | ||||
| 	spec->num_init_verbs = 1; | ||||
| 	spec->init_verbs[0] = cxt5066_init_verbs; | ||||
| 	spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes); | ||||
| 	spec->channel_mode = cxt5066_modes; | ||||
| 	spec->cur_adc = 0; | ||||
| 	spec->cur_adc_idx = 0; | ||||
| 
 | ||||
| 	board_config = snd_hda_check_board_config(codec, CXT5066_MODELS, | ||||
| 						  cxt5066_models, cxt5066_cfg_tbl); | ||||
| 	switch (board_config) { | ||||
| 	default: | ||||
| 	case CXT5066_LAPTOP: | ||||
| 		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; | ||||
| 		spec->mixers[spec->num_mixers++] = cxt5066_mixers; | ||||
| 		break; | ||||
| 	case CXT5066_DELL_LAPTOP: | ||||
| 		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; | ||||
| 		spec->mixers[spec->num_mixers++] = cxt5066_mixers; | ||||
| 
 | ||||
| 		spec->port_d_mode = PIN_OUT; | ||||
| 		spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo; | ||||
| 		spec->num_init_verbs++; | ||||
| 		spec->dell_automute = 1; | ||||
| 		break; | ||||
| 	case CXT5066_OLPC_XO_1_5: | ||||
| 		codec->patch_ops.unsol_event = cxt5066_unsol_event; | ||||
| 		spec->init_verbs[0] = cxt5066_init_verbs_olpc; | ||||
| 		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; | ||||
| 		spec->mixers[spec->num_mixers++] = cxt5066_mixers; | ||||
| 		spec->port_d_mode = 0; | ||||
| 
 | ||||
| 		/* no S/PDIF out */ | ||||
| 		spec->multiout.dig_out_nid = 0; | ||||
| 
 | ||||
| 		/* input source automatically selected */ | ||||
| 		spec->input_mux = NULL; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  */ | ||||
|  | @ -1919,12 +2395,15 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { | |||
| 	  .patch = patch_cxt5047 }, | ||||
| 	{ .id = 0x14f15051, .name = "CX20561 (Hermosa)", | ||||
| 	  .patch = patch_cxt5051 }, | ||||
| 	{ .id = 0x14f15066, .name = "CX20582 (Pebble)", | ||||
| 	  .patch = patch_cxt5066 }, | ||||
| 	{} /* terminator */ | ||||
| }; | ||||
| 
 | ||||
| MODULE_ALIAS("snd-hda-codec-id:14f15045"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:14f15047"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:14f15051"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:14f15066"); | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_DESCRIPTION("Conexant HD-audio codec"); | ||||
|  |  | |||
|  | @ -33,8 +33,8 @@ | |||
| #include "hda_codec.h" | ||||
| #include "hda_local.h" | ||||
| 
 | ||||
| #define CVT_NID		0x02	/* audio converter */ | ||||
| #define PIN_NID		0x03	/* HDMI output pin */ | ||||
| static hda_nid_t cvt_nid;	/* audio converter */ | ||||
| static hda_nid_t pin_nid;	/* HDMI output pin */ | ||||
| 
 | ||||
| #define INTEL_HDMI_EVENT_TAG		0x08 | ||||
| 
 | ||||
|  | @ -44,30 +44,6 @@ struct intel_hdmi_spec { | |||
| 	struct hdmi_eld sink_eld; | ||||
| }; | ||||
| 
 | ||||
| static struct hda_verb pinout_enable_verb[] = { | ||||
| 	{PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||||
| 	{} /* terminator */ | ||||
| }; | ||||
| 
 | ||||
| static struct hda_verb unsolicited_response_verb[] = { | ||||
| 	{PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | | ||||
| 						  INTEL_HDMI_EVENT_TAG}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static struct hda_verb def_chan_map[] = { | ||||
| 	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00}, | ||||
| 	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11}, | ||||
| 	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22}, | ||||
| 	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33}, | ||||
| 	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44}, | ||||
| 	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55}, | ||||
| 	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66}, | ||||
| 	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| struct hdmi_audio_infoframe { | ||||
| 	u8 type; /* 0x84 */ | ||||
| 	u8 ver;  /* 0x01 */ | ||||
|  | @ -244,11 +220,12 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid, | |||
| static void hdmi_enable_output(struct hda_codec *codec) | ||||
| { | ||||
| 	/* Unmute */ | ||||
| 	if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP) | ||||
| 		snd_hda_codec_write(codec, PIN_NID, 0, | ||||
| 	if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) | ||||
| 		snd_hda_codec_write(codec, pin_nid, 0, | ||||
| 				AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); | ||||
| 	/* Enable pin out */ | ||||
| 	snd_hda_sequence_write(codec, pinout_enable_verb); | ||||
| 	snd_hda_codec_write(codec, pin_nid, 0, | ||||
| 			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -256,8 +233,8 @@ static void hdmi_enable_output(struct hda_codec *codec) | |||
|  */ | ||||
| static void hdmi_start_infoframe_trans(struct hda_codec *codec) | ||||
| { | ||||
| 	hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0); | ||||
| 	snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT, | ||||
| 	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); | ||||
| 	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, | ||||
| 						AC_DIPXMIT_BEST); | ||||
| } | ||||
| 
 | ||||
|  | @ -266,20 +243,20 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec) | |||
|  */ | ||||
| static void hdmi_stop_infoframe_trans(struct hda_codec *codec) | ||||
| { | ||||
| 	hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0); | ||||
| 	snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT, | ||||
| 	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); | ||||
| 	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, | ||||
| 						AC_DIPXMIT_DISABLE); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_get_channel_count(struct hda_codec *codec) | ||||
| { | ||||
| 	return 1 + snd_hda_codec_read(codec, CVT_NID, 0, | ||||
| 	return 1 + snd_hda_codec_read(codec, cvt_nid, 0, | ||||
| 					AC_VERB_GET_CVT_CHAN_COUNT, 0); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_set_channel_count(struct hda_codec *codec, int chs) | ||||
| { | ||||
| 	snd_hda_codec_write(codec, CVT_NID, 0, | ||||
| 	snd_hda_codec_write(codec, cvt_nid, 0, | ||||
| 					AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); | ||||
| 
 | ||||
| 	if (chs != hdmi_get_channel_count(codec)) | ||||
|  | @ -294,7 +271,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec) | |||
| 	int slot; | ||||
| 
 | ||||
| 	for (i = 0; i < 8; i++) { | ||||
| 		slot = snd_hda_codec_read(codec, CVT_NID, 0, | ||||
| 		slot = snd_hda_codec_read(codec, cvt_nid, 0, | ||||
| 						AC_VERB_GET_HDMI_CHAN_SLOT, i); | ||||
| 		printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", | ||||
| 						slot >> 4, slot & 0x7); | ||||
|  | @ -307,7 +284,7 @@ static void hdmi_parse_eld(struct hda_codec *codec) | |||
| 	struct intel_hdmi_spec *spec = codec->spec; | ||||
| 	struct hdmi_eld *eld = &spec->sink_eld; | ||||
| 
 | ||||
| 	if (!snd_hdmi_get_eld(eld, codec, PIN_NID)) | ||||
| 	if (!snd_hdmi_get_eld(eld, codec, pin_nid)) | ||||
| 		snd_hdmi_show_eld(eld); | ||||
| } | ||||
| 
 | ||||
|  | @ -322,11 +299,11 @@ static void hdmi_debug_dip_size(struct hda_codec *codec) | |||
| 	int i; | ||||
| 	int size; | ||||
| 
 | ||||
| 	size = snd_hdmi_get_eld_size(codec, PIN_NID); | ||||
| 	size = snd_hdmi_get_eld_size(codec, pin_nid); | ||||
| 	printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size); | ||||
| 
 | ||||
| 	for (i = 0; i < 8; i++) { | ||||
| 		size = snd_hda_codec_read(codec, PIN_NID, 0, | ||||
| 		size = snd_hda_codec_read(codec, pin_nid, 0, | ||||
| 						AC_VERB_GET_HDMI_DIP_SIZE, i); | ||||
| 		printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size); | ||||
| 	} | ||||
|  | @ -340,15 +317,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec) | |||
| 	int size; | ||||
| 	int pi, bi; | ||||
| 	for (i = 0; i < 8; i++) { | ||||
| 		size = snd_hda_codec_read(codec, PIN_NID, 0, | ||||
| 		size = snd_hda_codec_read(codec, pin_nid, 0, | ||||
| 						AC_VERB_GET_HDMI_DIP_SIZE, i); | ||||
| 		if (size == 0) | ||||
| 			continue; | ||||
| 
 | ||||
| 		hdmi_set_dip_index(codec, PIN_NID, i, 0x0); | ||||
| 		hdmi_set_dip_index(codec, pin_nid, i, 0x0); | ||||
| 		for (j = 1; j < 1000; j++) { | ||||
| 			hdmi_write_dip_byte(codec, PIN_NID, 0x0); | ||||
| 			hdmi_get_dip_index(codec, PIN_NID, &pi, &bi); | ||||
| 			hdmi_write_dip_byte(codec, pin_nid, 0x0); | ||||
| 			hdmi_get_dip_index(codec, pin_nid, &pi, &bi); | ||||
| 			if (pi != i) | ||||
| 				snd_printd(KERN_INFO "dip index %d: %d != %d\n", | ||||
| 						bi, pi, i); | ||||
|  | @ -376,9 +353,9 @@ static void hdmi_fill_audio_infoframe(struct hda_codec *codec, | |||
| 		sum += params[i]; | ||||
| 	ai->checksum = - sum; | ||||
| 
 | ||||
| 	hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0); | ||||
| 	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); | ||||
| 	for (i = 0; i < sizeof(ai); i++) | ||||
| 		hdmi_write_dip_byte(codec, PIN_NID, params[i]); | ||||
| 		hdmi_write_dip_byte(codec, pin_nid, params[i]); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -465,6 +442,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, | |||
| static void hdmi_setup_channel_mapping(struct hda_codec *codec, | ||||
| 					struct hdmi_audio_infoframe *ai) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (!ai->CA) | ||||
| 		return; | ||||
| 
 | ||||
|  | @ -473,7 +452,11 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, | |||
| 	 * ALSA sequence is front/surr/clfe/side? | ||||
| 	 */ | ||||
| 
 | ||||
| 	snd_hda_sequence_write(codec, def_chan_map); | ||||
| 	for (i = 0; i < 8; i++) | ||||
| 		snd_hda_codec_write(codec, cvt_nid, 0, | ||||
| 				    AC_VERB_SET_HDMI_CHAN_SLOT, | ||||
| 				    (i << 4) | i); | ||||
| 
 | ||||
| 	hdmi_debug_channel_mapping(codec); | ||||
| } | ||||
| 
 | ||||
|  | @ -597,7 +580,6 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = { | |||
| 	.substreams = 1, | ||||
| 	.channels_min = 2, | ||||
| 	.channels_max = 8, | ||||
| 	.nid = CVT_NID, /* NID to query formats and rates and setup streams */ | ||||
| 	.ops = { | ||||
| 		.open    = intel_hdmi_playback_pcm_open, | ||||
| 		.close   = intel_hdmi_playback_pcm_close, | ||||
|  | @ -613,6 +595,9 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec) | |||
| 	codec->num_pcms = 1; | ||||
| 	codec->pcm_info = info; | ||||
| 
 | ||||
| 	/* NID to query formats and rates and setup streams */ | ||||
| 	intel_hdmi_pcm_playback.nid = cvt_nid; | ||||
| 
 | ||||
| 	info->name = "INTEL HDMI"; | ||||
| 	info->pcm_type = HDA_PCM_TYPE_HDMI; | ||||
| 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; | ||||
|  | @ -636,8 +621,9 @@ static int intel_hdmi_init(struct hda_codec *codec) | |||
| { | ||||
| 	hdmi_enable_output(codec); | ||||
| 
 | ||||
| 	snd_hda_sequence_write(codec, unsolicited_response_verb); | ||||
| 
 | ||||
| 	snd_hda_codec_write(codec, pin_nid, 0, | ||||
| 			    AC_VERB_SET_UNSOLICITED_ENABLE, | ||||
| 			    AC_USRSP_EN | INTEL_HDMI_EVENT_TAG); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -657,7 +643,7 @@ static struct hda_codec_ops intel_hdmi_patch_ops = { | |||
| 	.unsol_event		= intel_hdmi_unsol_event, | ||||
| }; | ||||
| 
 | ||||
| static int patch_intel_hdmi(struct hda_codec *codec) | ||||
| static int do_patch_intel_hdmi(struct hda_codec *codec) | ||||
| { | ||||
| 	struct intel_hdmi_spec *spec; | ||||
| 
 | ||||
|  | @ -667,7 +653,7 @@ static int patch_intel_hdmi(struct hda_codec *codec) | |||
| 
 | ||||
| 	spec->multiout.num_dacs = 0;	  /* no analog */ | ||||
| 	spec->multiout.max_channels = 8; | ||||
| 	spec->multiout.dig_out_nid = CVT_NID; | ||||
| 	spec->multiout.dig_out_nid = cvt_nid; | ||||
| 
 | ||||
| 	codec->spec = spec; | ||||
| 	codec->patch_ops = intel_hdmi_patch_ops; | ||||
|  | @ -679,12 +665,27 @@ static int patch_intel_hdmi(struct hda_codec *codec) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int patch_intel_hdmi(struct hda_codec *codec) | ||||
| { | ||||
| 	cvt_nid = 0x02; | ||||
| 	pin_nid = 0x03; | ||||
| 	return do_patch_intel_hdmi(codec); | ||||
| } | ||||
| 
 | ||||
| static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec) | ||||
| { | ||||
| 	cvt_nid = 0x02; | ||||
| 	pin_nid = 0x04; | ||||
| 	return do_patch_intel_hdmi(codec); | ||||
| } | ||||
| 
 | ||||
| static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { | ||||
| 	{ .id = 0x808629fb, .name = "G45 DEVCL",  .patch = patch_intel_hdmi }, | ||||
| 	{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, | ||||
| 	{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, | ||||
| 	{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, | ||||
| 	{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, | ||||
| 	{ .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, | ||||
| 	{ .id = 0x10951392, .name = "SiI1392 HDMI",     .patch = patch_intel_hdmi }, | ||||
| 	{} /* terminator */ | ||||
| }; | ||||
|  | @ -694,6 +695,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862801"); | |||
| MODULE_ALIAS("snd-hda-codec-id:80862802"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:80862803"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:80862804"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:80860054"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:10951392"); | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
|  |  | |||
|  | @ -377,6 +377,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) | |||
|  */ | ||||
| static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { | ||||
| 	{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, | ||||
| 	{ .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, | ||||
| 	{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, | ||||
| 	{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch }, | ||||
| 	{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, | ||||
|  | @ -385,6 +386,7 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { | |||
| }; | ||||
| 
 | ||||
| MODULE_ALIAS("snd-hda-codec-id:10de0002"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:10de0003"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:10de0006"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:10de0007"); | ||||
| MODULE_ALIAS("snd-hda-codec-id:10de0067"); | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1339,8 +1339,7 @@ static int get_mux_nids(struct hda_codec *codec) | |||
| 	for (i = 0; i < spec->num_adc_nids; i++) { | ||||
| 		nid = spec->adc_nids[i]; | ||||
| 		while (nid) { | ||||
| 			type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) | ||||
| 				>> AC_WCAP_TYPE_SHIFT; | ||||
| 			type = get_wcaps_type(get_wcaps(codec, nid)); | ||||
| 			if (type == AC_WID_PIN) | ||||
| 				break; | ||||
| 			n = snd_hda_get_connections(codec, nid, conn, | ||||
|  |  | |||
|  | @ -379,6 +379,15 @@ struct snd_ice1712 { | |||
| 	unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate); | ||||
| 	void (*set_spdif_clock)(struct snd_ice1712 *ice); | ||||
| 
 | ||||
| #ifdef CONFIG_PM | ||||
| 	int (*pm_suspend)(struct snd_ice1712 *); | ||||
| 	int (*pm_resume)(struct snd_ice1712 *); | ||||
| 	int pm_suspend_enabled:1; | ||||
| 	int pm_saved_is_spdif_master:1; | ||||
| 	unsigned int pm_saved_spdif_ctrl; | ||||
| 	unsigned char pm_saved_spdif_cfg; | ||||
| 	unsigned int pm_saved_route; | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -560,6 +560,7 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
| 
 | ||||
| 	case SNDRV_PCM_TRIGGER_START: | ||||
| 	case SNDRV_PCM_TRIGGER_STOP: | ||||
| 	case SNDRV_PCM_TRIGGER_SUSPEND: | ||||
| 		spin_lock(&ice->reg_lock); | ||||
| 		old = inb(ICEMT1724(ice, DMA_CONTROL)); | ||||
| 		if (cmd == SNDRV_PCM_TRIGGER_START) | ||||
|  | @ -570,6 +571,10 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
| 		spin_unlock(&ice->reg_lock); | ||||
| 		break; | ||||
| 
 | ||||
| 	case SNDRV_PCM_TRIGGER_RESUME: | ||||
| 		/* apps will have to restart stream */ | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | @ -2262,7 +2267,7 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice, | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice) | ||||
| static void snd_vt1724_chip_reset(struct snd_ice1712 *ice) | ||||
| { | ||||
| 	outb(VT1724_RESET , ICEREG1724(ice, CONTROL)); | ||||
| 	inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */ | ||||
|  | @ -2272,7 +2277,7 @@ static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice) | |||
| 	msleep(10); | ||||
| } | ||||
| 
 | ||||
| static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice) | ||||
| static int snd_vt1724_chip_init(struct snd_ice1712 *ice) | ||||
| { | ||||
| 	outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG)); | ||||
| 	outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG)); | ||||
|  | @ -2287,6 +2292,14 @@ static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice) | |||
| 
 | ||||
| 	outb(0, ICEREG1724(ice, POWERDOWN)); | ||||
| 
 | ||||
| 	/* MPU_RX and TX irq masks are cleared later dynamically */ | ||||
| 	outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK)); | ||||
| 
 | ||||
| 	/* don't handle FIFO overrun/underruns (just yet),
 | ||||
| 	 * since they cause machine lockups | ||||
| 	 */ | ||||
| 	outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -2431,6 +2444,8 @@ static int __devinit snd_vt1724_create(struct snd_card *card, | |||
| 	snd_vt1724_proc_init(ice); | ||||
| 	synchronize_irq(pci->irq); | ||||
| 
 | ||||
| 	card->private_data = ice; | ||||
| 
 | ||||
| 	err = pci_request_regions(pci, "ICE1724"); | ||||
| 	if (err < 0) { | ||||
| 		kfree(ice); | ||||
|  | @ -2459,14 +2474,6 @@ static int __devinit snd_vt1724_create(struct snd_card *card, | |||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	/* MPU_RX and TX irq masks are cleared later dynamically */ | ||||
| 	outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK)); | ||||
| 
 | ||||
| 	/* don't handle FIFO overrun/underruns (just yet),
 | ||||
| 	 * since they cause machine lockups | ||||
| 	 */ | ||||
| 	outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK)); | ||||
| 
 | ||||
| 	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops); | ||||
| 	if (err < 0) { | ||||
| 		snd_vt1724_free(ice); | ||||
|  | @ -2650,11 +2657,96 @@ static void __devexit snd_vt1724_remove(struct pci_dev *pci) | |||
| 	pci_set_drvdata(pci, NULL); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_PM | ||||
| static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state) | ||||
| { | ||||
| 	struct snd_card *card = pci_get_drvdata(pci); | ||||
| 	struct snd_ice1712 *ice = card->private_data; | ||||
| 
 | ||||
| 	if (!ice->pm_suspend_enabled) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | ||||
| 
 | ||||
| 	snd_pcm_suspend_all(ice->pcm); | ||||
| 	snd_pcm_suspend_all(ice->pcm_pro); | ||||
| 	snd_pcm_suspend_all(ice->pcm_ds); | ||||
| 	snd_ac97_suspend(ice->ac97); | ||||
| 
 | ||||
| 	spin_lock_irq(&ice->reg_lock); | ||||
| 	ice->pm_saved_is_spdif_master = ice->is_spdif_master(ice); | ||||
| 	ice->pm_saved_spdif_ctrl = inw(ICEMT1724(ice, SPDIF_CTRL)); | ||||
| 	ice->pm_saved_spdif_cfg = inb(ICEREG1724(ice, SPDIF_CFG)); | ||||
| 	ice->pm_saved_route = inl(ICEMT1724(ice, ROUTE_PLAYBACK)); | ||||
| 	spin_unlock_irq(&ice->reg_lock); | ||||
| 
 | ||||
| 	if (ice->pm_suspend) | ||||
| 		ice->pm_suspend(ice); | ||||
| 
 | ||||
| 	pci_disable_device(pci); | ||||
| 	pci_save_state(pci); | ||||
| 	pci_set_power_state(pci, pci_choose_state(pci, state)); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int snd_vt1724_resume(struct pci_dev *pci) | ||||
| { | ||||
| 	struct snd_card *card = pci_get_drvdata(pci); | ||||
| 	struct snd_ice1712 *ice = card->private_data; | ||||
| 
 | ||||
| 	if (!ice->pm_suspend_enabled) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	pci_set_power_state(pci, PCI_D0); | ||||
| 	pci_restore_state(pci); | ||||
| 
 | ||||
| 	if (pci_enable_device(pci) < 0) { | ||||
| 		snd_card_disconnect(card); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	pci_set_master(pci); | ||||
| 
 | ||||
| 	snd_vt1724_chip_reset(ice); | ||||
| 
 | ||||
| 	if (snd_vt1724_chip_init(ice) < 0) { | ||||
| 		snd_card_disconnect(card); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ice->pm_resume) | ||||
| 		ice->pm_resume(ice); | ||||
| 
 | ||||
| 	if (ice->pm_saved_is_spdif_master) { | ||||
| 		/* switching to external clock via SPDIF */ | ||||
| 		ice->set_spdif_clock(ice); | ||||
| 	} else { | ||||
| 		/* internal on-card clock */ | ||||
| 		snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1); | ||||
| 	} | ||||
| 
 | ||||
| 	update_spdif_bits(ice, ice->pm_saved_spdif_ctrl); | ||||
| 
 | ||||
| 	outb(ice->pm_saved_spdif_cfg, ICEREG1724(ice, SPDIF_CFG)); | ||||
| 	outl(ice->pm_saved_route, ICEMT1724(ice, ROUTE_PLAYBACK)); | ||||
| 
 | ||||
| 	if (ice->ac97) | ||||
| 		snd_ac97_resume(ice->ac97); | ||||
| 
 | ||||
| 	snd_power_change_state(card, SNDRV_CTL_POWER_D0); | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static struct pci_driver driver = { | ||||
| 	.name = "ICE1724", | ||||
| 	.id_table = snd_vt1724_ids, | ||||
| 	.probe = snd_vt1724_probe, | ||||
| 	.remove = __devexit_p(snd_vt1724_remove), | ||||
| #ifdef CONFIG_PM | ||||
| 	.suspend = snd_vt1724_suspend, | ||||
| 	.resume = snd_vt1724_resume, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static int __init alsa_card_ice1724_init(void) | ||||
|  |  | |||
|  | @ -1077,7 +1077,7 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice) | |||
| /*
 | ||||
|  * initialize the chip | ||||
|  */ | ||||
| static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) | ||||
| static void ak4396_init(struct snd_ice1712 *ice) | ||||
| { | ||||
| 	static unsigned short ak4396_inits[] = { | ||||
| 		AK4396_CTRL1,	   0x87,   /* I2S Normal Mode, 24 bit */ | ||||
|  | @ -1087,9 +1087,37 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) | |||
| 		AK4396_RCH_ATT,	 0x00, | ||||
| 	}; | ||||
| 
 | ||||
| 	struct prodigy_hifi_spec *spec; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	/* initialize ak4396 codec */ | ||||
| 	/* reset codec */ | ||||
| 	ak4396_write(ice, AK4396_CTRL1, 0x86); | ||||
| 	msleep(100); | ||||
| 	ak4396_write(ice, AK4396_CTRL1, 0x87); | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2) | ||||
| 		ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_PM | ||||
| static int __devinit prodigy_hd2_resume(struct snd_ice1712 *ice) | ||||
| { | ||||
| 	/* initialize ak4396 codec and restore previous mixer volumes */ | ||||
| 	struct prodigy_hifi_spec *spec = ice->spec; | ||||
| 	int i; | ||||
| 	mutex_lock(&ice->gpio_mutex); | ||||
| 	ak4396_init(ice); | ||||
| 	for (i = 0; i < 2; i++) | ||||
| 		ak4396_write(ice, AK4396_LCH_ATT + i, spec->vol[i] & 0xff); | ||||
| 	mutex_unlock(&ice->gpio_mutex); | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) | ||||
| { | ||||
| 	struct prodigy_hifi_spec *spec; | ||||
| 
 | ||||
| 	ice->vt1720 = 0; | ||||
| 	ice->vt1724 = 1; | ||||
| 
 | ||||
|  | @ -1112,14 +1140,12 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) | |||
| 		return -ENOMEM; | ||||
| 	ice->spec = spec; | ||||
| 
 | ||||
| 	/* initialize ak4396 codec */ | ||||
| 	/* reset codec */ | ||||
| 	ak4396_write(ice, AK4396_CTRL1, 0x86); | ||||
| 	msleep(100); | ||||
| 	ak4396_write(ice, AK4396_CTRL1, 0x87); | ||||
| #ifdef CONFIG_PM | ||||
| 	ice->pm_resume = &prodigy_hd2_resume; | ||||
| 	ice->pm_suspend_enabled = 1; | ||||
| #endif | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2) | ||||
| 		ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]); | ||||
| 	ak4396_init(ice); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -215,17 +215,8 @@ EXPORT_SYMBOL(oxygen_write_spi); | |||
| 
 | ||||
| void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data) | ||||
| { | ||||
| 	unsigned long timeout; | ||||
| 
 | ||||
| 	/* should not need more than about 300 us */ | ||||
| 	timeout = jiffies + msecs_to_jiffies(1); | ||||
| 	do { | ||||
| 		if (!(oxygen_read16(chip, OXYGEN_2WIRE_BUS_STATUS) | ||||
| 		      & OXYGEN_2WIRE_BUSY)) | ||||
| 			break; | ||||
| 		udelay(1); | ||||
| 		cond_resched(); | ||||
| 	} while (time_after_eq(timeout, jiffies)); | ||||
| 	msleep(1); | ||||
| 
 | ||||
| 	oxygen_write8(chip, OXYGEN_2WIRE_MAP, map); | ||||
| 	oxygen_write8(chip, OXYGEN_2WIRE_DATA, data); | ||||
|  |  | |||
|  | @ -3294,15 +3294,33 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) | |||
| 	char *clock_source; | ||||
| 	int x; | ||||
| 
 | ||||
| 	if (hdsp_check_for_iobox (hdsp)) { | ||||
| 		snd_iprintf(buffer, "No I/O box connected.\nPlease connect one and upload firmware.\n"); | ||||
| 	status = hdsp_read(hdsp, HDSP_statusRegister); | ||||
| 	status2 = hdsp_read(hdsp, HDSP_status2Register); | ||||
| 
 | ||||
| 	snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name, | ||||
| 		    hdsp->card->number + 1); | ||||
| 	snd_iprintf(buffer, "Buffers: capture %p playback %p\n", | ||||
| 		    hdsp->capture_buffer, hdsp->playback_buffer); | ||||
| 	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", | ||||
| 		    hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase); | ||||
| 	snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register); | ||||
| 	snd_iprintf(buffer, "Control2 register: 0x%x\n", | ||||
| 		    hdsp->control2_register); | ||||
| 	snd_iprintf(buffer, "Status register: 0x%x\n", status); | ||||
| 	snd_iprintf(buffer, "Status2 register: 0x%x\n", status2); | ||||
| 
 | ||||
| 	if (hdsp_check_for_iobox(hdsp)) { | ||||
| 		snd_iprintf(buffer, "No I/O box connected.\n" | ||||
| 			    "Please connect one and upload firmware.\n"); | ||||
| 		return; | ||||
|         } | ||||
| 	} | ||||
| 
 | ||||
| 	if (hdsp_check_for_firmware(hdsp, 0)) { | ||||
| 		if (hdsp->state & HDSP_FirmwareCached) { | ||||
| 			if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) { | ||||
| 				snd_iprintf(buffer, "Firmware loading from cache failed, please upload manually.\n"); | ||||
| 				snd_iprintf(buffer, "Firmware loading from " | ||||
| 					    "cache failed, " | ||||
| 					    "please upload manually.\n"); | ||||
| 				return; | ||||
| 			} | ||||
| 		} else { | ||||
|  | @ -3319,18 +3337,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	status = hdsp_read(hdsp, HDSP_statusRegister); | ||||
| 	status2 = hdsp_read(hdsp, HDSP_status2Register); | ||||
| 
 | ||||
| 	snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name, hdsp->card->number + 1); | ||||
| 	snd_iprintf(buffer, "Buffers: capture %p playback %p\n", | ||||
| 		    hdsp->capture_buffer, hdsp->playback_buffer); | ||||
| 	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", | ||||
| 		    hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase); | ||||
| 	snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register); | ||||
| 	snd_iprintf(buffer, "Control2 register: 0x%x\n", hdsp->control2_register); | ||||
| 	snd_iprintf(buffer, "Status register: 0x%x\n", status); | ||||
| 	snd_iprintf(buffer, "Status2 register: 0x%x\n", status2); | ||||
| 	snd_iprintf(buffer, "FIFO status: %d\n", hdsp_read(hdsp, HDSP_fifoStatus) & 0xff); | ||||
| 	snd_iprintf(buffer, "MIDI1 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut0)); | ||||
| 	snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0)); | ||||
|  | @ -3351,7 +3357,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) | |||
| 
 | ||||
| 	snd_iprintf(buffer, "\n"); | ||||
| 
 | ||||
| 
 | ||||
| 	switch (hdsp_clock_source(hdsp)) { | ||||
| 	case HDSP_CLOCK_SOURCE_AUTOSYNC: | ||||
| 		clock_source = "AutoSync"; | ||||
|  |  | |||
|  | @ -834,7 +834,7 @@ static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id) | |||
| 	status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG); | ||||
| 	if (status & 1) { | ||||
| 		if (chip->timer) | ||||
| 			snd_timer_interrupt(chip->timer, chip->timer->sticks); | ||||
| 			snd_timer_interrupt(chip->timer, chip->timer_ticks); | ||||
| 	} | ||||
| 	snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status); | ||||
| 
 | ||||
|  | @ -1885,8 +1885,18 @@ static int snd_ymfpci_timer_start(struct snd_timer *timer) | |||
| 	unsigned int count; | ||||
| 
 | ||||
| 	chip = snd_timer_chip(timer); | ||||
| 	count = (timer->sticks << 1) - 1; | ||||
| 	spin_lock_irqsave(&chip->reg_lock, flags); | ||||
| 	if (timer->sticks > 1) { | ||||
| 		chip->timer_ticks = timer->sticks; | ||||
| 		count = timer->sticks - 1; | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * Divisor 1 is not allowed; fake it by using divisor 2 and | ||||
| 		 * counting two ticks for each interrupt. | ||||
| 		 */ | ||||
| 		chip->timer_ticks = 2; | ||||
| 		count = 2 - 1; | ||||
| 	} | ||||
| 	snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count); | ||||
| 	snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03); | ||||
| 	spin_unlock_irqrestore(&chip->reg_lock, flags); | ||||
|  | @ -1909,14 +1919,14 @@ static int snd_ymfpci_timer_precise_resolution(struct snd_timer *timer, | |||
| 					       unsigned long *num, unsigned long *den) | ||||
| { | ||||
| 	*num = 1; | ||||
| 	*den = 48000; | ||||
| 	*den = 96000; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct snd_timer_hardware snd_ymfpci_timer_hw = { | ||||
| 	.flags = SNDRV_TIMER_HW_AUTO, | ||||
| 	.resolution = 20833, /* 1/fs = 20.8333...us */ | ||||
| 	.ticks = 0x8000, | ||||
| 	.resolution = 10417, /* 1 / 96 kHz = 10.41666...us */ | ||||
| 	.ticks = 0x10000, | ||||
| 	.start = snd_ymfpci_timer_start, | ||||
| 	.stop = snd_ymfpci_timer_stop, | ||||
| 	.precise_resolution = snd_ymfpci_timer_precise_resolution, | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ source "sound/soc/au1x/Kconfig" | |||
| source "sound/soc/blackfin/Kconfig" | ||||
| source "sound/soc/davinci/Kconfig" | ||||
| source "sound/soc/fsl/Kconfig" | ||||
| source "sound/soc/imx/Kconfig" | ||||
| source "sound/soc/omap/Kconfig" | ||||
| source "sound/soc/pxa/Kconfig" | ||||
| source "sound/soc/s3c24xx/Kconfig" | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o | ||||
| snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o | ||||
| 
 | ||||
| obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o | ||||
| obj-$(CONFIG_SND_SOC)	+= codecs/ | ||||
|  | @ -7,6 +7,7 @@ obj-$(CONFIG_SND_SOC)	+= au1x/ | |||
| obj-$(CONFIG_SND_SOC)	+= blackfin/ | ||||
| obj-$(CONFIG_SND_SOC)	+= davinci/ | ||||
| obj-$(CONFIG_SND_SOC)	+= fsl/ | ||||
| obj-$(CONFIG_SND_SOC)   += imx/ | ||||
| obj-$(CONFIG_SND_SOC)	+= omap/ | ||||
| obj-$(CONFIG_SND_SOC)	+= pxa/ | ||||
| obj-$(CONFIG_SND_SOC)	+= s3c24xx/ | ||||
|  |  | |||
|  | @ -56,133 +56,32 @@ | |||
| 
 | ||||
| #define MCLK_RATE 12000000 | ||||
| 
 | ||||
| /*
 | ||||
|  * As shipped the board does not have inputs.  However, it is relatively | ||||
|  * straightforward to modify the board to hook them up so support is left | ||||
|  * in the driver. | ||||
|  */ | ||||
| #undef ENABLE_MIC_INPUT | ||||
| 
 | ||||
| static struct clk *mclk; | ||||
| 
 | ||||
| static int at91sam9g20ek_startup(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); | ||||
| 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, | ||||
| 		MCLK_RATE, SND_SOC_CLOCK_IN); | ||||
| 	if (ret < 0) { | ||||
| 		clk_disable(mclk); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream) | ||||
| { | ||||
| 	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); | ||||
| 
 | ||||
| 	dev_dbg(rtd->socdev->dev, "shutdown"); | ||||
| } | ||||
| 
 | ||||
| static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, | ||||
| 	struct snd_pcm_hw_params *params) | ||||
| { | ||||
| 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||||
| 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||||
| 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||||
| 	struct atmel_ssc_info *ssc_p = cpu_dai->private_data; | ||||
| 	struct ssc_device *ssc = ssc_p->ssc; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	unsigned int rate; | ||||
| 	int cmr_div, period; | ||||
| 
 | ||||
| 	if (ssc == NULL) { | ||||
| 		printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	/* set codec DAI configuration */ | ||||
| 	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||||
| 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||||
| 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* set cpu DAI configuration */ | ||||
| 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||||
| 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The SSC clock dividers depend on the sample rate.  The CMR.DIV | ||||
| 	 * field divides the system master clock MCK to drive the SSC TK | ||||
| 	 * signal which provides the codec BCLK.  The TCMR.PERIOD and | ||||
| 	 * RCMR.PERIOD fields further divide the BCLK signal to drive | ||||
| 	 * the SSC TF and RF signals which provide the codec DACLRC and | ||||
| 	 * ADCLRC clocks. | ||||
| 	 * | ||||
| 	 * The dividers were determined through trial and error, where a | ||||
| 	 * CMR.DIV value is chosen such that the resulting BCLK value is | ||||
| 	 * divisible, or almost divisible, by (2 * sample rate), and then | ||||
| 	 * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1. | ||||
| 	 */ | ||||
| 	rate = params_rate(params); | ||||
| 
 | ||||
| 	switch (rate) { | ||||
| 	case 8000: | ||||
| 		cmr_div = 55;	/* BCLK = 133MHz/(2*55) = 1.209MHz */ | ||||
| 		period = 74;	/* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */ | ||||
| 		break; | ||||
| 	case 11025: | ||||
| 		cmr_div = 67;	/* BCLK = 133MHz/(2*60) = 1.108MHz */ | ||||
| 		period = 45;	/* LRC = BCLK/(2*(49+1)) = 11083,3Hz */ | ||||
| 		break; | ||||
| 	case 16000: | ||||
| 		cmr_div = 63;	/* BCLK = 133MHz/(2*63) = 1.055MHz */ | ||||
| 		period = 32;	/* LRC = BCLK/(2*(32+1)) = 15993,2Hz */ | ||||
| 		break; | ||||
| 	case 22050: | ||||
| 		cmr_div = 52;	/* BCLK = 133MHz/(2*52) = 1.278MHz */ | ||||
| 		period = 28;	/* LRC = BCLK/(2*(28+1)) = 22049Hz */ | ||||
| 		break; | ||||
| 	case 32000: | ||||
| 		cmr_div = 66;	/* BCLK = 133MHz/(2*66) = 1.007MHz */ | ||||
| 		period = 15;	/* LRC = BCLK/(2*(15+1)) = 31486,742Hz */ | ||||
| 		break; | ||||
| 	case 44100: | ||||
| 		cmr_div = 29;	/* BCLK = 133MHz/(2*29) = 2.293MHz */ | ||||
| 		period = 25;	/* LRC = BCLK/(2*(25+1)) = 44098Hz */ | ||||
| 		break; | ||||
| 	case 48000: | ||||
| 		cmr_div = 33;	/* BCLK = 133MHz/(2*33) = 2.015MHz */ | ||||
| 		period = 20;	/* LRC = BCLK/(2*(20+1)) = 47979,79Hz */ | ||||
| 		break; | ||||
| 	case 88200: | ||||
| 		cmr_div = 29;	/* BCLK = 133MHz/(2*29) = 2.293MHz */ | ||||
| 		period = 12;	/* LRC = BCLK/(2*(12+1)) = 88196Hz */ | ||||
| 		break; | ||||
| 	case 96000: | ||||
| 		cmr_div = 23;	/* BCLK = 133MHz/(2*23) = 2.891MHz */ | ||||
| 		period = 14;	/* LRC = BCLK/(2*(14+1)) = 96376Hz */ | ||||
| 		break; | ||||
| 	default: | ||||
| 		printk(KERN_WARNING "unsupported rate %d" | ||||
| 				" on at91sam9g20ek board\n", rate); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	/* set the MCK divider for BCLK */ | ||||
| 	ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||||
| 		/* set the BCLK divider for DACLRC */ | ||||
| 		ret = snd_soc_dai_set_clkdiv(cpu_dai, | ||||
| 						ATMEL_SSC_TCMR_PERIOD, period); | ||||
| 	} else { | ||||
| 		/* set the BCLK divider for ADCLRC */ | ||||
| 		ret = snd_soc_dai_set_clkdiv(cpu_dai, | ||||
| 						ATMEL_SSC_RCMR_PERIOD, period); | ||||
| 	} | ||||
| 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
|  | @ -190,9 +89,7 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream, | |||
| } | ||||
| 
 | ||||
| static struct snd_soc_ops at91sam9g20ek_ops = { | ||||
| 	.startup = at91sam9g20ek_startup, | ||||
| 	.hw_params = at91sam9g20ek_hw_params, | ||||
| 	.shutdown = at91sam9g20ek_shutdown, | ||||
| }; | ||||
| 
 | ||||
| static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card, | ||||
|  | @ -241,10 +138,20 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
|  */ | ||||
| static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec) | ||||
| { | ||||
| 	struct snd_soc_dai *codec_dai = &codec->dai[0]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	printk(KERN_DEBUG | ||||
| 			"at91sam9g20ek_wm8731 " | ||||
| 			": at91sam9g20ek_wm8731_init() called\n"); | ||||
| 
 | ||||
| 	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, | ||||
| 		MCLK_RATE, SND_SOC_CLOCK_IN); | ||||
| 	if (ret < 0) { | ||||
| 		printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Add specific widgets */ | ||||
| 	snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets, | ||||
| 				  ARRAY_SIZE(at91sam9g20ek_dapm_widgets)); | ||||
|  | @ -255,8 +162,13 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec) | |||
| 	snd_soc_dapm_nc_pin(codec, "RLINEIN"); | ||||
| 	snd_soc_dapm_nc_pin(codec, "LLINEIN"); | ||||
| 
 | ||||
| 	/* always connected */ | ||||
| #ifdef ENABLE_MIC_INPUT | ||||
| 	snd_soc_dapm_enable_pin(codec, "Int Mic"); | ||||
| #else | ||||
| 	snd_soc_dapm_nc_pin(codec, "Int Mic"); | ||||
| #endif | ||||
| 
 | ||||
| 	/* always connected */ | ||||
| 	snd_soc_dapm_enable_pin(codec, "Ext Spk"); | ||||
| 
 | ||||
| 	snd_soc_dapm_sync(codec); | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| /*
 | ||||
|  * Au12x0/Au1550 PSC ALSA ASoC audio support. | ||||
|  * | ||||
|  * (c) 2007-2008 MSC Vertriebsges.m.b.H., | ||||
|  *	Manuel Lauss <mano@roarinelk.homelinux.net> | ||||
|  * (c) 2007-2009 MSC Vertriebsges.m.b.H., | ||||
|  *	Manuel Lauss <manuel.lauss@gmail.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  | @ -19,6 +19,7 @@ | |||
| #include <linux/module.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/suspend.h> | ||||
| #include <sound/core.h> | ||||
| #include <sound/pcm.h> | ||||
|  | @ -29,6 +30,9 @@ | |||
| 
 | ||||
| #include "psc.h" | ||||
| 
 | ||||
| /* how often to retry failed codec register reads/writes */ | ||||
| #define AC97_RW_RETRIES	5 | ||||
| 
 | ||||
| #define AC97_DIR	\ | ||||
| 	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) | ||||
| 
 | ||||
|  | @ -45,6 +49,9 @@ | |||
| #define AC97PCR_CLRFIFO(stype)	\ | ||||
| 	((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC) | ||||
| 
 | ||||
| #define AC97STAT_BUSY(stype)	\ | ||||
| 	((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB) | ||||
| 
 | ||||
| /* instance data. There can be only one, MacLeod!!!! */ | ||||
| static struct au1xpsc_audio_data *au1xpsc_ac97_workdata; | ||||
| 
 | ||||
|  | @ -54,24 +61,33 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, | |||
| { | ||||
| 	/* FIXME */ | ||||
| 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | ||||
| 	unsigned short data, tmo; | ||||
| 
 | ||||
| 	au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata)); | ||||
| 	au_sync(); | ||||
| 
 | ||||
| 	tmo = 1000; | ||||
| 	while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo) | ||||
| 		udelay(2); | ||||
| 
 | ||||
| 	if (!tmo) | ||||
| 		data = 0xffff; | ||||
| 	else | ||||
| 		data = au_readl(AC97_CDC(pscdata)) & 0xffff; | ||||
| 	unsigned short data, retry, tmo; | ||||
| 
 | ||||
| 	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | ||||
| 	au_sync(); | ||||
| 
 | ||||
| 	return data; | ||||
| 	retry = AC97_RW_RETRIES; | ||||
| 	do { | ||||
| 		mutex_lock(&pscdata->lock); | ||||
| 
 | ||||
| 		au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), | ||||
| 			  AC97_CDC(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		tmo = 2000; | ||||
| 		while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) | ||||
| 			&& --tmo) | ||||
| 			udelay(2); | ||||
| 
 | ||||
| 		data = au_readl(AC97_CDC(pscdata)) & 0xffff; | ||||
| 
 | ||||
| 		au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		mutex_unlock(&pscdata->lock); | ||||
| 	} while (--retry && !tmo); | ||||
| 
 | ||||
| 	return retry ? data : 0xffff; | ||||
| } | ||||
| 
 | ||||
| /* AC97 controller writes to codec register */ | ||||
|  | @ -80,16 +96,29 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | |||
| { | ||||
| 	/* FIXME */ | ||||
| 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | ||||
| 	unsigned int tmo; | ||||
| 
 | ||||
| 	au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata)); | ||||
| 	au_sync(); | ||||
| 	tmo = 1000; | ||||
| 	while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo) | ||||
| 		au_sync(); | ||||
| 	unsigned int tmo, retry; | ||||
| 
 | ||||
| 	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | ||||
| 	au_sync(); | ||||
| 
 | ||||
| 	retry = AC97_RW_RETRIES; | ||||
| 	do { | ||||
| 		mutex_lock(&pscdata->lock); | ||||
| 
 | ||||
| 		au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), | ||||
| 			  AC97_CDC(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		tmo = 2000; | ||||
| 		while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) | ||||
| 		       && --tmo) | ||||
| 			udelay(2); | ||||
| 
 | ||||
| 		au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		mutex_unlock(&pscdata->lock); | ||||
| 	} while (--retry && !tmo); | ||||
| } | ||||
| 
 | ||||
| /* AC97 controller asserts a warm reset */ | ||||
|  | @ -129,9 +158,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97) | |||
| 	au_sync(); | ||||
| 
 | ||||
| 	/* wait for PSC to indicate it's ready */ | ||||
| 	i = 100000; | ||||
| 	i = 1000; | ||||
| 	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i)) | ||||
| 		au_sync(); | ||||
| 		msleep(1); | ||||
| 
 | ||||
| 	if (i == 0) { | ||||
| 		printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n"); | ||||
|  | @ -143,9 +172,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97) | |||
| 	au_sync(); | ||||
| 
 | ||||
| 	/* wait for AC97 core to become ready */ | ||||
| 	i = 100000; | ||||
| 	i = 1000; | ||||
| 	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i)) | ||||
| 		au_sync(); | ||||
| 		msleep(1); | ||||
| 	if (i == 0) | ||||
| 		printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n"); | ||||
| } | ||||
|  | @ -165,12 +194,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
| { | ||||
| 	/* FIXME */ | ||||
| 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | ||||
| 	unsigned long r, stat; | ||||
| 	unsigned long r, ro, stat; | ||||
| 	int chans, stype = SUBSTREAM_TYPE(substream); | ||||
| 
 | ||||
| 	chans = params_channels(params); | ||||
| 
 | ||||
| 	r = au_readl(AC97_CFG(pscdata)); | ||||
| 	r = ro = au_readl(AC97_CFG(pscdata)); | ||||
| 	stat = au_readl(AC97_STAT(pscdata)); | ||||
| 
 | ||||
| 	/* already active? */ | ||||
|  | @ -180,9 +209,6 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
| 		    (pscdata->rate != params_rate(params))) | ||||
| 			return -EINVAL; | ||||
| 	} else { | ||||
| 		/* disable AC97 device controller first */ | ||||
| 		au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */ | ||||
| 		r &= ~PSC_AC97CFG_LEN_MASK; | ||||
|  | @ -199,14 +225,40 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
| 			r |= PSC_AC97CFG_RXSLOT_ENA(4); | ||||
| 		} | ||||
| 
 | ||||
| 		/* finally enable the AC97 controller again */ | ||||
| 		/* do we need to poke the hardware? */ | ||||
| 		if (!(r ^ ro)) | ||||
| 			goto out; | ||||
| 
 | ||||
| 		/* ac97 engine is about to be disabled */ | ||||
| 		mutex_lock(&pscdata->lock); | ||||
| 
 | ||||
| 		/* disable AC97 device controller first... */ | ||||
| 		au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		/* ...wait for it... */ | ||||
| 		while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) | ||||
| 			asm volatile ("nop"); | ||||
| 
 | ||||
| 		/* ...write config... */ | ||||
| 		au_writel(r, AC97_CFG(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		/* ...enable the AC97 controller again... */ | ||||
| 		au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		/* ...and wait for ready bit */ | ||||
| 		while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) | ||||
| 			asm volatile ("nop"); | ||||
| 
 | ||||
| 		mutex_unlock(&pscdata->lock); | ||||
| 
 | ||||
| 		pscdata->cfg = r; | ||||
| 		pscdata->rate = params_rate(params); | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -222,6 +274,8 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |||
| 	switch (cmd) { | ||||
| 	case SNDRV_PCM_TRIGGER_START: | ||||
| 	case SNDRV_PCM_TRIGGER_RESUME: | ||||
| 		au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata)); | ||||
| 		au_sync(); | ||||
| 		au_writel(AC97PCR_START(stype), AC97_PCR(pscdata)); | ||||
| 		au_sync(); | ||||
| 		break; | ||||
|  | @ -229,6 +283,13 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |||
| 	case SNDRV_PCM_TRIGGER_SUSPEND: | ||||
| 		au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype)) | ||||
| 			asm volatile ("nop"); | ||||
| 
 | ||||
| 		au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata)); | ||||
| 		au_sync(); | ||||
| 
 | ||||
| 		break; | ||||
| 	default: | ||||
| 		ret = -EINVAL; | ||||
|  | @ -251,6 +312,8 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev, | |||
| 	if (!au1xpsc_ac97_workdata) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	mutex_init(&au1xpsc_ac97_workdata->lock); | ||||
| 
 | ||||
| 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	if (!r) { | ||||
| 		ret = -ENODEV; | ||||
|  | @ -269,9 +332,9 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev, | |||
| 		goto out1; | ||||
| 
 | ||||
| 	/* configuration: max dma trigger threshold, enable ac97 */ | ||||
| 	 au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | | ||||
| 				      PSC_AC97CFG_TT_FIFO8 | | ||||
| 				      PSC_AC97CFG_DE_ENABLE; | ||||
| 	au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | | ||||
| 				     PSC_AC97CFG_TT_FIFO8 | | ||||
| 				     PSC_AC97CFG_DE_ENABLE; | ||||
| 
 | ||||
| 	/* preserve PSC clock source set up by platform (dev.platform_data
 | ||||
| 	 * is already occupied by soc layer) | ||||
|  | @ -386,4 +449,4 @@ module_exit(au1xpsc_ac97_exit); | |||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); | ||||
| MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); | ||||
| MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>"); | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Linus Torvalds
				Linus Torvalds