Merge branch 'for-next' into topic/hda-core

This commit is contained in:
Takashi Iwai 2015-03-23 13:14:02 +01:00
commit 3372dbdd8c
166 changed files with 4294 additions and 5515 deletions

View file

@ -18,6 +18,7 @@ Required properties:
* Headphones
* Speakers
* Mic Jack
* Int Mic
- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's
connected to the CODEC.

View file

@ -466,7 +466,11 @@ The generic parser supports the following hints:
- add_jack_modes (bool): add "xxx Jack Mode" enum controls to each
I/O jack for allowing to change the headphone amp and mic bias VREF
capabilities
- power_down_unused (bool): power down the unused widgets
- power_save_node (bool): advanced power management for each widget,
controlling the power sate (D0/D3) of each widget node depending on
the actual pin and stream states
- power_down_unused (bool): power down the unused widgets, a subset of
power_save_node, and will be dropped in future
- add_hp_mic (bool): add the headphone to capture source if possible
- hp_mic_detect (bool): enable/disable the hp/mic shared input for a
single built-in mic case; default true

View file

@ -0,0 +1,200 @@
The ALSA API can provide two different system timestamps:
- Trigger_tstamp is the system time snapshot taken when the .trigger
callback is invoked. This snapshot is taken by the ALSA core in the
general case, but specific hardware may have synchronization
capabilities or conversely may only be able to provide a correct
estimate with a delay. In the latter two cases, the low-level driver
is responsible for updating the trigger_tstamp at the most appropriate
and precise moment. Applications should not rely solely on the first
trigger_tstamp but update their internal calculations if the driver
provides a refined estimate with a delay.
- tstamp is the current system timestamp updated during the last
event or application query.
The difference (tstamp - trigger_tstamp) defines the elapsed time.
The ALSA API provides reports two basic pieces of information, avail
and delay, which combined with the trigger and current system
timestamps allow for applications to keep track of the 'fullness' of
the ring buffer and the amount of queued samples.
The use of these different pointers and time information depends on
the application needs:
- 'avail' reports how much can be written in the ring buffer
- 'delay' reports the time it will take to hear a new sample after all
queued samples have been played out.
When timestamps are enabled, the avail/delay information is reported
along with a snapshot of system time. Applications can select from
CLOCK_REALTIME (NTP corrections including going backwards),
CLOCK_MONOTONIC (NTP corrections but never going backwards),
CLOCK_MONOTIC_RAW (without NTP corrections) and change the mode
dynamically with sw_params
The ALSA API also provide an audio_tstamp which reflects the passage
of time as measured by different components of audio hardware. In
ascii-art, this could be represented as follows (for the playback
case):
--------------------------------------------------------------> time
^ ^ ^ ^ ^
| | | | |
analog link dma app FullBuffer
time time time time time
| | | | |
|< codec delay >|<--hw delay-->|<queued samples>|<---avail->|
|<----------------- delay---------------------->| |
|<----ring buffer length---->|
The analog time is taken at the last stage of the playback, as close
as possible to the actual transducer
The link time is taken at the output of the SOC/chipset as the samples
are pushed on a link. The link time can be directly measured if
supported in hardware by sample counters or wallclocks (e.g. with
HDAudio 24MHz or PTP clock for networked solutions) or indirectly
estimated (e.g. with the frame counter in USB).
The DMA time is measured using counters - typically the least reliable
of all measurements due to the bursty natured of DMA transfers.
The app time corresponds to the time tracked by an application after
writing in the ring buffer.
The application can query what the hardware supports, define which
audio time it wants reported by selecting the relevant settings in
audio_tstamp_config fields, get an estimate of the timestamp
accuracy. It can also request the delay-to-analog be included in the
measurement. Direct access to the link time is very interesting on
platforms that provide an embedded DSP; measuring directly the link
time with dedicated hardware, possibly synchronized with system time,
removes the need to keep track of internal DSP processing times and
latency.
In case the application requests an audio tstamp that is not supported
in hardware/low-level driver, the type is overridden as DEFAULT and the
timestamp will report the DMA time based on the hw_pointer value.
For backwards compatibility with previous implementations that did not
provide timestamp selection, with a zero-valued COMPAT timestamp type
the results will default to the HDAudio wall clock for playback
streams and to the DMA time (hw_ptr) in all other cases.
The audio timestamp accuracy can be returned to user-space, so that
appropriate decisions are made:
- for dma time (default), the granularity of the transfers can be
inferred from the steps between updates and in turn provide
information on how much the application pointer can be rewound
safely.
- the link time can be used to track long-term drifts between audio
and system time using the (tstamp-trigger_tstamp)/audio_tstamp
ratio, the precision helps define how much smoothing/low-pass
filtering is required. The link time can be either reset on startup
or reported as is (the latter being useful to compare progress of
different streams - but may require the wallclock to be always
running and not wrap-around during idle periods). If supported in
hardware, the absolute link time could also be used to define a
precise start time (patches WIP)
- including the delay in the audio timestamp may
counter-intuitively not increase the precision of timestamps, e.g. if a
codec includes variable-latency DSP processing or a chain of
hardware components the delay is typically not known with precision.
The accuracy is reported in nanosecond units (using an unsigned 32-bit
word), which gives a max precision of 4.29s, more than enough for
audio applications...
Due to the varied nature of timestamping needs, even for a single
application, the audio_tstamp_config can be changed dynamically. In
the STATUS ioctl, the parameters are read-only and do not allow for
any application selection. To work around this limitation without
impacting legacy applications, a new STATUS_EXT ioctl is introduced
with read/write parameters. ALSA-lib will be modified to make use of
STATUS_EXT and effectively deprecate STATUS.
The ALSA API only allows for a single audio timestamp to be reported
at a time. This is a conscious design decision, reading the audio
timestamps from hardware registers or from IPC takes time, the more
timestamps are read the more imprecise the combined measurements
are. To avoid any interpretation issues, a single (system, audio)
timestamp is reported. Applications that need different timestamps
will be required to issue multiple queries and perform an
interpolation of the results
In some hardware-specific configuration, the system timestamp is
latched by a low-level audio subsytem, and the information provided
back to the driver. Due to potential delays in the communication with
the hardware, there is a risk of misalignment with the avail and delay
information. To make sure applications are not confused, a
driver_timestamp field is added in the snd_pcm_status structure; this
timestamp shows when the information is put together by the driver
before returning from the STATUS and STATUS_EXT ioctl. in most cases
this driver_timestamp will be identical to the regular system tstamp.
Examples of typestamping with HDaudio:
1. DMA timestamp, no compensation for DMA+analog delay
$ ./audio_time -p --ts_type=1
playback: systime: 341121338 nsec, audio time 342000000 nsec, systime delta -878662
playback: systime: 426236663 nsec, audio time 427187500 nsec, systime delta -950837
playback: systime: 597080580 nsec, audio time 598000000 nsec, systime delta -919420
playback: systime: 682059782 nsec, audio time 683020833 nsec, systime delta -961051
playback: systime: 852896415 nsec, audio time 853854166 nsec, systime delta -957751
playback: systime: 937903344 nsec, audio time 938854166 nsec, systime delta -950822
2. DMA timestamp, compensation for DMA+analog delay
$ ./audio_time -p --ts_type=1 -d
playback: systime: 341053347 nsec, audio time 341062500 nsec, systime delta -9153
playback: systime: 426072447 nsec, audio time 426062500 nsec, systime delta 9947
playback: systime: 596899518 nsec, audio time 596895833 nsec, systime delta 3685
playback: systime: 681915317 nsec, audio time 681916666 nsec, systime delta -1349
playback: systime: 852741306 nsec, audio time 852750000 nsec, systime delta -8694
3. link timestamp, compensation for DMA+analog delay
$ ./audio_time -p --ts_type=2 -d
playback: systime: 341060004 nsec, audio time 341062791 nsec, systime delta -2787
playback: systime: 426242074 nsec, audio time 426244875 nsec, systime delta -2801
playback: systime: 597080992 nsec, audio time 597084583 nsec, systime delta -3591
playback: systime: 682084512 nsec, audio time 682088291 nsec, systime delta -3779
playback: systime: 852936229 nsec, audio time 852940916 nsec, systime delta -4687
playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -5146
Example 1 shows that the timestamp at the DMA level is close to 1ms
ahead of the actual playback time (as a side time this sort of
measurement can help define rewind safeguards). Compensating for the
DMA-link delay in example 2 helps remove the hardware buffering abut
the information is still very jittery, with up to one sample of
error. In example 3 where the timestamps are measured with the link
wallclock, the timestamps show a monotonic behavior and a lower
dispersion.
Example 3 and 4 are with USB audio class. Example 3 shows a high
offset between audio time and system time due to buffering. Example 4
shows how compensating for the delay exposes a 1ms accuracy (due to
the use of the frame counter by the driver)
Example 3: DMA timestamp, no compensation for delay, delta of ~5ms
$ ./audio_time -p -Dhw:1 -t1
playback: systime: 120174019 nsec, audio time 125000000 nsec, systime delta -4825981
playback: systime: 245041136 nsec, audio time 250000000 nsec, systime delta -4958864
playback: systime: 370106088 nsec, audio time 375000000 nsec, systime delta -4893912
playback: systime: 495040065 nsec, audio time 500000000 nsec, systime delta -4959935
playback: systime: 620038179 nsec, audio time 625000000 nsec, systime delta -4961821
playback: systime: 745087741 nsec, audio time 750000000 nsec, systime delta -4912259
playback: systime: 870037336 nsec, audio time 875000000 nsec, systime delta -4962664
Example 4: DMA timestamp, compensation for delay, delay of ~1ms
$ ./audio_time -p -Dhw:1 -t1 -d
playback: systime: 120190520 nsec, audio time 120000000 nsec, systime delta 190520
playback: systime: 245036740 nsec, audio time 244000000 nsec, systime delta 1036740
playback: systime: 370034081 nsec, audio time 369000000 nsec, systime delta 1034081
playback: systime: 495159907 nsec, audio time 494000000 nsec, systime delta 1159907
playback: systime: 620098824 nsec, audio time 619000000 nsec, systime delta 1098824
playback: systime: 745031847 nsec, audio time 744000000 nsec, systime delta 1031847

View file

@ -70,7 +70,7 @@ struct snd_compr_runtime {
* @device: device pointer
* @direction: stream direction, playback/recording
* @metadata_set: metadata set flag, true when set
* @next_track: has userspace signall next track transistion, true when set
* @next_track: has userspace signal next track transition, true when set
* @private_data: pointer to DSP private data
*/
struct snd_compr_stream {
@ -95,7 +95,7 @@ struct snd_compr_stream {
* and the stream properties
* @get_params: retrieve the codec parameters, mandatory
* @set_metadata: Set the metadata values for a stream
* @get_metadata: retreives the requested metadata values from stream
* @get_metadata: retrieves the requested metadata values from stream
* @trigger: Trigger operations like start, pause, resume, drain, stop.
* This callback is mandatory
* @pointer: Retrieve current h/w pointer information. Mandatory

View file

@ -227,7 +227,7 @@ snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
* Add a virtual slave control to the given master.
* Unlike snd_ctl_add_slave(), the element added via this function
* is supposed to have volatile values, and get callback is called
* at each time quried from the master.
* at each time queried from the master.
*
* When the control peeks the hardware values directly and the value
* can be changed by other means than the put callback of the element,

View file

@ -278,7 +278,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type,
void *device_data, struct snd_device_ops *ops);
int snd_device_register(struct snd_card *card, void *device_data);
int snd_device_register_all(struct snd_card *card);
int snd_device_disconnect_all(struct snd_card *card);
void snd_device_disconnect(struct snd_card *card, void *device_data);
void snd_device_disconnect_all(struct snd_card *card);
void snd_device_free(struct snd_card *card, void *device_data);
void snd_device_free_all(struct snd_card *card);

View file

@ -60,6 +60,9 @@ struct snd_pcm_hardware {
struct snd_pcm_substream;
struct snd_pcm_audio_tstamp_config; /* definitions further down */
struct snd_pcm_audio_tstamp_report;
struct snd_pcm_ops {
int (*open)(struct snd_pcm_substream *substream);
int (*close)(struct snd_pcm_substream *substream);
@ -71,8 +74,10 @@ struct snd_pcm_ops {
int (*prepare)(struct snd_pcm_substream *substream);
int (*trigger)(struct snd_pcm_substream *substream, int cmd);
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
int (*wall_clock)(struct snd_pcm_substream *substream,
struct timespec *audio_ts);
int (*get_time_info)(struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report);
int (*copy)(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos,
void __user *buf, snd_pcm_uframes_t count);
@ -281,6 +286,58 @@ struct snd_pcm_hw_constraint_ranges {
struct snd_pcm_hwptr_log;
/*
* userspace-provided audio timestamp config to kernel,
* structure is for internal use only and filled with dedicated unpack routine
*/
struct snd_pcm_audio_tstamp_config {
/* 5 of max 16 bits used */
u32 type_requested:4;
u32 report_delay:1; /* add total delay to A/D or D/A */
};
static inline void snd_pcm_unpack_audio_tstamp_config(__u32 data,
struct snd_pcm_audio_tstamp_config *config)
{
config->type_requested = data & 0xF;
config->report_delay = (data >> 4) & 1;
}
/*
* kernel-provided audio timestamp report to user-space
* structure is for internal use only and read by dedicated pack routine
*/
struct snd_pcm_audio_tstamp_report {
/* 6 of max 16 bits used for bit-fields */
/* for backwards compatibility */
u32 valid:1;
/* actual type if hardware could not support requested timestamp */
u32 actual_type:4;
/* accuracy represented in ns units */
u32 accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */
u32 accuracy; /* up to 4.29s, will be packed in separate field */
};
static inline void snd_pcm_pack_audio_tstamp_report(__u32 *data, __u32 *accuracy,
const struct snd_pcm_audio_tstamp_report *report)
{
u32 tmp;
tmp = report->accuracy_report;
tmp <<= 4;
tmp |= report->actual_type;
tmp <<= 1;
tmp |= report->valid;
*data &= 0xffff; /* zero-clear MSBs */
*data |= (tmp << 16);
*accuracy = report->accuracy;
}
struct snd_pcm_runtime {
/* -- Status -- */
struct snd_pcm_substream *trigger_master;
@ -361,6 +418,11 @@ struct snd_pcm_runtime {
struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */
/* -- audio timestamp config -- */
struct snd_pcm_audio_tstamp_config audio_tstamp_config;
struct snd_pcm_audio_tstamp_report audio_tstamp_report;
struct timespec driver_tstamp;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
/* -- OSS things -- */
struct snd_pcm_oss_runtime oss;

View file

@ -366,4 +366,11 @@ static inline int params_physical_width(const struct snd_pcm_hw_params *p)
return snd_pcm_format_physical_width(params_format(p));
}
static inline void
params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt)
{
snd_mask_set(hw_param_mask(p, SNDRV_PCM_HW_PARAM_FORMAT),
(__force int)fmt);
}
#endif /* __SOUND_PCM_PARAMS_H */

View file

@ -25,29 +25,26 @@
* registered device information
*/
#define ID_LEN 32
/* status flag */
#define SNDRV_SEQ_DEVICE_FREE 0
#define SNDRV_SEQ_DEVICE_REGISTERED 1
struct snd_seq_device {
/* device info */
struct snd_card *card; /* sound card */
int device; /* device number */
char id[ID_LEN]; /* driver id */
const char *id; /* driver id */
char name[80]; /* device name */
int argsize; /* size of the argument */
void *driver_data; /* private data for driver */
int status; /* flag - read only */
void *private_data; /* private data for the caller */
void (*private_free)(struct snd_seq_device *device);
struct list_head list; /* link to next device */
struct device dev;
};
#define to_seq_dev(_dev) \
container_of(_dev, struct snd_seq_device, dev)
/* sequencer driver */
/* driver operators
* init_device:
* probe:
* Initialize the device with given parameters.
* Typically,
* 1. call snd_hwdep_new
@ -55,25 +52,40 @@ struct snd_seq_device {
* 3. call snd_hwdep_register
* 4. store the instance to dev->driver_data pointer.
*
* free_device:
* remove:
* Release the private data.
* Typically, call snd_device_free(dev->card, dev->driver_data)
*/
struct snd_seq_dev_ops {
int (*init_device)(struct snd_seq_device *dev);
int (*free_device)(struct snd_seq_device *dev);
struct snd_seq_driver {
struct device_driver driver;
char *id;
int argsize;
};
#define to_seq_drv(_drv) \
container_of(_drv, struct snd_seq_driver, driver)
/*
* prototypes
*/
#ifdef CONFIG_MODULES
void snd_seq_device_load_drivers(void);
int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result);
int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize);
int snd_seq_device_unregister_driver(char *id);
#else
#define snd_seq_device_load_drivers()
#endif
int snd_seq_device_new(struct snd_card *card, int device, const char *id,
int argsize, struct snd_seq_device **result);
#define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(struct snd_seq_device))
int __must_check __snd_seq_driver_register(struct snd_seq_driver *drv,
struct module *mod);
#define snd_seq_driver_register(drv) \
__snd_seq_driver_register(drv, THIS_MODULE)
void snd_seq_driver_unregister(struct snd_seq_driver *drv);
#define module_snd_seq_driver(drv) \
module_driver(drv, snd_seq_driver_register, snd_seq_driver_unregister)
/*
* id strings for generic devices

View file

@ -99,13 +99,9 @@ int snd_seq_event_port_attach(int client, struct snd_seq_port_callback *pcbp,
int snd_seq_event_port_detach(int client, int port);
#ifdef CONFIG_MODULES
void snd_seq_autoload_lock(void);
void snd_seq_autoload_unlock(void);
void snd_seq_autoload_init(void);
#define snd_seq_autoload_exit() snd_seq_autoload_lock()
void snd_seq_autoload_exit(void);
#else
#define snd_seq_autoload_lock()
#define snd_seq_autoload_unlock()
#define snd_seq_autoload_init()
#define snd_seq_autoload_exit()
#endif

View file

@ -450,8 +450,10 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
/* Jack reporting */
int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
struct snd_soc_jack *jack);
int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins,
unsigned int num_pins);
void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask);
int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_pin *pins);
@ -659,7 +661,7 @@ struct snd_soc_jack_gpio {
struct snd_soc_jack {
struct mutex mutex;
struct snd_jack *jack;
struct snd_soc_codec *codec;
struct snd_soc_card *card;
struct list_head pins;
int status;
struct blocking_notifier_head notifier;
@ -954,6 +956,9 @@ struct snd_soc_dai_link {
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
/* Mark this pcm with non atomic ops */
bool nonatomic;
/* Do not create a PCM for this DAI link (Backend link) */
unsigned int no_pcm:1;
@ -1071,11 +1076,16 @@ struct snd_soc_card {
/*
* Card-specific routes and widgets.
* Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in.
*/
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
const struct snd_soc_dapm_widget *of_dapm_widgets;
int num_of_dapm_widgets;
const struct snd_soc_dapm_route *of_dapm_routes;
int num_of_dapm_routes;
bool fully_routed;
struct work_struct deferred_resume_work;
@ -1469,7 +1479,7 @@ static inline struct snd_soc_codec *snd_soc_kcontrol_codec(
}
/**
* snd_soc_kcontrol_platform() - Returns the platform that registerd the control
* snd_soc_kcontrol_platform() - Returns the platform that registered the control
* @kcontrol: The control for which to get the platform
*
* Note: This function will only work correctly if the control has been

View file

@ -22,6 +22,7 @@
#ifndef _UAPI__SOUND_ASEQUENCER_H
#define _UAPI__SOUND_ASEQUENCER_H
#include <sound/asound.h>
/** version of the sequencer */
#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1)

View file

@ -25,6 +25,9 @@
#include <linux/types.h>
#ifndef __KERNEL__
#include <stdlib.h>
#endif
/*
* protocol version
@ -140,7 +143,7 @@ struct snd_hwdep_dsp_image {
* *
*****************************************************************************/
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 12)
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 13)
typedef unsigned long snd_pcm_uframes_t;
typedef signed long snd_pcm_sframes_t;
@ -267,10 +270,17 @@ typedef int __bitwise snd_pcm_subformat_t;
#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */
#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */
#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */
#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* has audio wall clock for audio/system time sync */
#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* (Deprecated)has audio wall clock for audio/system time sync */
#define SNDRV_PCM_INFO_HAS_LINK_ATIME 0x01000000 /* report hardware link audio time, reset on startup */
#define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */
#define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */
#define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */
#define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */
#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
typedef int __bitwise snd_pcm_state_t;
#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */
#define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */
@ -408,6 +418,22 @@ struct snd_pcm_channel_info {
unsigned int step; /* samples distance in bits */
};
enum {
/*
* first definition for backwards compatibility only,
* maps to wallclock/link time for HDAudio playback and DEFAULT/DMA time for everything else
*/
SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT = 0,
/* timestamp definitions */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT = 1, /* DMA time, reported as per hw_ptr */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK = 2, /* link time reported by sample or wallclock counter, reset on startup */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE = 3, /* link time reported by sample or wallclock counter, not reset on startup */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED = 4, /* link time estimated indirectly */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED = 5, /* link time synchronized with system time */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED
};
struct snd_pcm_status {
snd_pcm_state_t state; /* stream state */
struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */
@ -419,9 +445,11 @@ struct snd_pcm_status {
snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */
snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */
snd_pcm_state_t suspended_state; /* suspended stream state */
__u32 reserved_alignment; /* must be filled with zero */
struct timespec audio_tstamp; /* from sample counter or wall clock */
unsigned char reserved[56-sizeof(struct timespec)]; /* must be filled with zero */
__u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */
struct timespec audio_tstamp; /* sample counter, wall clock, PHC or on-demand sync'ed */
struct timespec driver_tstamp; /* useful in case reference system tstamp is reported with delay */
__u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */
unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled with zero */
};
struct snd_pcm_mmap_status {
@ -534,6 +562,7 @@ enum {
#define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t)
#define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22)
#define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr)
#define SNDRV_PCM_IOCTL_STATUS_EXT _IOWR('A', 0x24, struct snd_pcm_status)
#define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info)
#define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40)
#define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41)

View file

@ -75,7 +75,7 @@ struct snd_compr_tstamp {
/**
* struct snd_compr_avail - avail descriptor
* @avail: Number of bytes available in ring buffer for writing/reading
* @tstamp: timestamp infomation
* @tstamp: timestamp information
*/
struct snd_compr_avail {
__u64 avail;

View file

@ -23,8 +23,7 @@
#define _UAPI__SOUND_EMU10K1_H
#include <linux/types.h>
#include <sound/asound.h>
/*
* ---- FX8010 ----

View file

@ -20,6 +20,12 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
/* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */
#define HDSPM_MAX_CHANNELS 64

View file

@ -31,7 +31,7 @@ module_param(force, int, 0444);
MODULE_PARM_DESC(force, "Force loading i2sbus even when"
" no layout-id property is present");
static struct of_device_id i2sbus_match[] = {
static const struct of_device_id i2sbus_match[] = {
{ .name = "i2s" },
{ }
};

View file

@ -192,36 +192,41 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
EXPORT_SYMBOL(snd_ctl_notify);
/**
* snd_ctl_new - create a control instance from the template
* @control: the control template
* @access: the default control access
* snd_ctl_new - create a new control instance with some elements
* @kctl: the pointer to store new control instance
* @count: the number of elements in this control
* @access: the default access flags for elements in this control
* @file: given when locking these elements
*
* Allocates a new struct snd_kcontrol instance and copies the given template
* to the new instance. It does not copy volatile data (access).
* Allocates a memory object for a new control instance. The instance has
* elements as many as the given number (@count). Each element has given
* access permissions (@access). Each element is locked when @file is given.
*
* Return: The pointer of the new instance, or %NULL on failure.
* Return: 0 on success, error code on failure
*/
static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
unsigned int access)
static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
unsigned int access, struct snd_ctl_file *file)
{
struct snd_kcontrol *kctl;
unsigned int size;
unsigned int idx;
if (snd_BUG_ON(!control || !control->count))
return NULL;
if (count == 0 || count > MAX_CONTROL_COUNT)
return -EINVAL;
if (control->count > MAX_CONTROL_COUNT)
return NULL;
size = sizeof(struct snd_kcontrol);
size += sizeof(struct snd_kcontrol_volatile) * count;
kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
if (kctl == NULL) {
pr_err("ALSA: Cannot allocate control instance\n");
return NULL;
*kctl = kzalloc(size, GFP_KERNEL);
if (!*kctl)
return -ENOMEM;
for (idx = 0; idx < count; idx++) {
(*kctl)->vd[idx].access = access;
(*kctl)->vd[idx].owner = file;
}
*kctl = *control;
for (idx = 0; idx < kctl->count; idx++)
kctl->vd[idx].access = access;
return kctl;
(*kctl)->count = count;
return 0;
}
/**
@ -238,37 +243,53 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
void *private_data)
{
struct snd_kcontrol kctl;
struct snd_kcontrol *kctl;
unsigned int count;
unsigned int access;
int err;
if (snd_BUG_ON(!ncontrol || !ncontrol->info))
return NULL;
memset(&kctl, 0, sizeof(kctl));
kctl.id.iface = ncontrol->iface;
kctl.id.device = ncontrol->device;
kctl.id.subdevice = ncontrol->subdevice;
count = ncontrol->count;
if (count == 0)
count = 1;
access = ncontrol->access;
if (access == 0)
access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_VOLATILE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE |
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
err = snd_ctl_new(&kctl, count, access, NULL);
if (err < 0)
return NULL;
/* The 'numid' member is decided when calling snd_ctl_add(). */
kctl->id.iface = ncontrol->iface;
kctl->id.device = ncontrol->device;
kctl->id.subdevice = ncontrol->subdevice;
if (ncontrol->name) {
strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
if (strcmp(ncontrol->name, kctl.id.name) != 0)
strlcpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name));
if (strcmp(ncontrol->name, kctl->id.name) != 0)
pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
ncontrol->name, kctl.id.name);
ncontrol->name, kctl->id.name);
}
kctl.id.index = ncontrol->index;
kctl.count = ncontrol->count ? ncontrol->count : 1;
access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_VOLATILE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
kctl.info = ncontrol->info;
kctl.get = ncontrol->get;
kctl.put = ncontrol->put;
kctl.tlv.p = ncontrol->tlv.p;
kctl.private_value = ncontrol->private_value;
kctl.private_data = private_data;
return snd_ctl_new(&kctl, access);
kctl->id.index = ncontrol->index;
kctl->info = ncontrol->info;
kctl->get = ncontrol->get;
kctl->put = ncontrol->put;
kctl->tlv.p = ncontrol->tlv.p;
kctl->private_value = ncontrol->private_value;
kctl->private_data = private_data;
return kctl;
}
EXPORT_SYMBOL(snd_ctl_new1);
@ -1161,84 +1182,102 @@ static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
static int snd_ctl_elem_add(struct snd_ctl_file *file,
struct snd_ctl_elem_info *info, int replace)
{
/* The capacity of struct snd_ctl_elem_value.value.*/
static const unsigned int value_sizes[] = {
[SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long),
[SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long),
[SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int),
[SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char),
[SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958),
[SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long),
};
static const unsigned int max_value_counts[] = {
[SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128,
[SNDRV_CTL_ELEM_TYPE_INTEGER] = 128,
[SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128,
[SNDRV_CTL_ELEM_TYPE_BYTES] = 512,
[SNDRV_CTL_ELEM_TYPE_IEC958] = 1,
[SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64,
};
struct snd_card *card = file->card;
struct snd_kcontrol kctl, *_kctl;
struct snd_kcontrol *kctl;
unsigned int count;
unsigned int access;
long private_size;
struct user_element *ue;
int idx, err;
int err;
if (info->count < 1)
return -EINVAL;
if (!*info->id.name)
return -EINVAL;
if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name))
return -EINVAL;
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
info->id.numid = 0;
memset(&kctl, 0, sizeof(kctl));
/* Delete a control to replace them if needed. */
if (replace) {
info->id.numid = 0;
err = snd_ctl_remove_user_ctl(file, &info->id);
if (err)
return err;
}
if (card->user_ctl_count >= MAX_USER_CONTROLS)
/*
* The number of userspace controls are counted control by control,
* not element by element.
*/
if (card->user_ctl_count + 1 > MAX_USER_CONTROLS)
return -ENOMEM;
memcpy(&kctl.id, &info->id, sizeof(info->id));
kctl.count = info->owner ? info->owner : 1;
access |= SNDRV_CTL_ELEM_ACCESS_USER;
if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
kctl.info = snd_ctl_elem_user_enum_info;
else
kctl.info = snd_ctl_elem_user_info;
if (access & SNDRV_CTL_ELEM_ACCESS_READ)
kctl.get = snd_ctl_elem_user_get;
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
kctl.put = snd_ctl_elem_user_put;
if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
kctl.tlv.c = snd_ctl_elem_user_tlv;
/* Check the number of elements for this userspace control. */
count = info->owner;
if (count == 0)
count = 1;
/* Arrange access permissions if needed. */
access = info->access;
if (access == 0)
access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE |
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
}
switch (info->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
private_size = sizeof(long);
if (info->count > 128)
return -EINVAL;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
private_size = sizeof(long long);
if (info->count > 64)
return -EINVAL;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
private_size = sizeof(unsigned int);
if (info->count > 128 || info->value.enumerated.items == 0)
return -EINVAL;
break;
case SNDRV_CTL_ELEM_TYPE_BYTES:
private_size = sizeof(unsigned char);
if (info->count > 512)
return -EINVAL;
break;
case SNDRV_CTL_ELEM_TYPE_IEC958:
private_size = sizeof(struct snd_aes_iec958);
if (info->count != 1)
return -EINVAL;
break;
default:
access |= SNDRV_CTL_ELEM_ACCESS_USER;
/*
* Check information and calculate the size of data specific to
* this userspace control.
*/
if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64)
return -EINVAL;
}
private_size *= info->count;
ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
if (ue == NULL)
if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
info->value.enumerated.items == 0)
return -EINVAL;
if (info->count < 1 ||
info->count > max_value_counts[info->type])
return -EINVAL;
private_size = value_sizes[info->type] * info->count;
/*
* Keep memory object for this userspace control. After passing this
* code block, the instance should be freed by snd_ctl_free_one().
*
* Note that these elements in this control are locked.
*/
err = snd_ctl_new(&kctl, count, access, file);
if (err < 0)
return err;
memcpy(&kctl->id, &info->id, sizeof(kctl->id));
kctl->private_data = kzalloc(sizeof(struct user_element) + private_size,
GFP_KERNEL);
if (kctl->private_data == NULL) {
kfree(kctl);
return -ENOMEM;
}
kctl->private_free = snd_ctl_elem_user_free;
/* Set private data for this userspace control. */
ue = (struct user_element *)kctl->private_data;
ue->card = card;
ue->info = *info;
ue->info.access = 0;
@ -1247,21 +1286,25 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
err = snd_ctl_elem_init_enum_names(ue);
if (err < 0) {
kfree(ue);
snd_ctl_free_one(kctl);
return err;
}
}
kctl.private_free = snd_ctl_elem_user_free;
_kctl = snd_ctl_new(&kctl, access);
if (_kctl == NULL) {
kfree(ue->priv_data);
kfree(ue);
return -ENOMEM;
}
_kctl->private_data = ue;
for (idx = 0; idx < _kctl->count; idx++)
_kctl->vd[idx].owner = file;
err = snd_ctl_add(card, _kctl);
/* Set callback functions. */
if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
kctl->info = snd_ctl_elem_user_enum_info;
else
kctl->info = snd_ctl_elem_user_info;
if (access & SNDRV_CTL_ELEM_ACCESS_READ)
kctl->get = snd_ctl_elem_user_get;
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
kctl->put = snd_ctl_elem_user_put;
if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
kctl->tlv.c = snd_ctl_elem_user_tlv;
/* This function manage to free the instance on failure. */
err = snd_ctl_add(card, kctl);
if (err < 0)
return err;

View file

@ -50,10 +50,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type,
if (snd_BUG_ON(!card || !device_data || !ops))
return -ENXIO;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(card->dev, "Cannot allocate device, type=%d\n", type);
if (!dev)
return -ENOMEM;
}
INIT_LIST_HEAD(&dev->list);
dev->card = card;
dev->type = type;
@ -73,7 +71,7 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type,
}
EXPORT_SYMBOL(snd_device_new);
static int __snd_device_disconnect(struct snd_device *dev)
static void __snd_device_disconnect(struct snd_device *dev)
{
if (dev->state == SNDRV_DEV_REGISTERED) {
if (dev->ops->dev_disconnect &&
@ -81,7 +79,6 @@ static int __snd_device_disconnect(struct snd_device *dev)
dev_err(dev->card->dev, "device disconnect failure\n");
dev->state = SNDRV_DEV_DISCONNECTED;
}
return 0;
}
static void __snd_device_free(struct snd_device *dev)
@ -108,6 +105,34 @@ static struct snd_device *look_for_dev(struct snd_card *card, void *device_data)
return NULL;
}
/**
* snd_device_disconnect - disconnect the device
* @card: the card instance
* @device_data: the data pointer to disconnect
*
* Turns the device into the disconnection state, invoking
* dev_disconnect callback, if the device was already registered.
*
* Usually called from snd_card_disconnect().
*
* Return: Zero if successful, or a negative error code on failure or if the
* device not found.
*/
void snd_device_disconnect(struct snd_card *card, void *device_data)
{
struct snd_device *dev;
if (snd_BUG_ON(!card || !device_data))
return;
dev = look_for_dev(card, device_data);
if (dev)
__snd_device_disconnect(dev);
else
dev_dbg(card->dev, "device disconnect %p (from %pF), not found\n",
device_data, __builtin_return_address(0));
}
EXPORT_SYMBOL_GPL(snd_device_disconnect);
/**
* snd_device_free - release the device from the card
* @card: the card instance
@ -195,18 +220,14 @@ int snd_device_register_all(struct snd_card *card)
* disconnect all the devices on the card.
* called from init.c
*/
int snd_device_disconnect_all(struct snd_card *card)
void snd_device_disconnect_all(struct snd_card *card)
{
struct snd_device *dev;
int err = 0;
if (snd_BUG_ON(!card))
return -ENXIO;
list_for_each_entry_reverse(dev, &card->devices, list) {
if (__snd_device_disconnect(dev) < 0)
err = -ENXIO;
}
return err;
return;
list_for_each_entry_reverse(dev, &card->devices, list)
__snd_device_disconnect(dev);
}
/*

View file

@ -378,10 +378,8 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
if (rhwdep)
*rhwdep = NULL;
hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
if (hwdep == NULL) {
dev_err(card->dev, "hwdep: cannot allocate\n");
if (!hwdep)
return -ENOMEM;
}
init_waitqueue_head(&hwdep->open_wait);
mutex_init(&hwdep->open_mutex);

View file

@ -400,7 +400,6 @@ static const struct file_operations snd_shutdown_f_ops =
int snd_card_disconnect(struct snd_card *card)
{
struct snd_monitor_file *mfile;
int err;
if (!card)
return -EINVAL;
@ -445,9 +444,7 @@ int snd_card_disconnect(struct snd_card *card)
#endif
/* notify all devices that we are disconnected */
err = snd_device_disconnect_all(card);
if (err < 0)
dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number);
snd_device_disconnect_all(card);
snd_info_card_disconnect(card);
if (card->registered) {

View file

@ -1212,10 +1212,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
/* not changed */
goto __unlock;
tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
if (! tbl) {
pr_err("ALSA: mixer_oss: no memory\n");
if (!tbl)
goto __unlock;
}
tbl->oss_id = ch;
tbl->name = kstrdup(str, GFP_KERNEL);
if (! tbl->name) {

View file

@ -854,7 +854,6 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
params = kmalloc(sizeof(*params), GFP_KERNEL);
sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
if (!sw_params || !params || !sparams) {
pcm_dbg(substream->pcm, "No memory\n");
err = -ENOMEM;
goto failure;
}

View file

@ -49,8 +49,6 @@ static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
struct snd_pcm *pcm;
list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->internal)
continue;
if (pcm->card == card && pcm->device == device)
return pcm;
}
@ -62,8 +60,6 @@ static int snd_pcm_next(struct snd_card *card, int device)
struct snd_pcm *pcm;
list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->internal)
continue;
if (pcm->card == card && pcm->device > device)
return pcm->device;
else if (pcm->card->number > card->number)
@ -76,6 +72,9 @@ static int snd_pcm_add(struct snd_pcm *newpcm)
{
struct snd_pcm *pcm;
if (newpcm->internal)
return 0;
list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->card == newpcm->card && pcm->device == newpcm->device)
return -EBUSY;
@ -344,11 +343,8 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
return;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (! info) {
pcm_dbg(substream->pcm,
"snd_pcm_proc_info_read: cannot malloc\n");
if (!info)
return;
}
err = snd_pcm_info(substream, info);
if (err < 0) {
@ -718,10 +714,8 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
prev = NULL;
for (idx = 0, prev = NULL; idx < substream_count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
if (substream == NULL) {
pcm_err(pcm, "Cannot allocate PCM substream\n");
if (!substream)
return -ENOMEM;
}
substream->pcm = pcm;
substream->pstr = pstr;
substream->number = idx;
@ -775,13 +769,14 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
if (rpcm)
*rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (pcm == NULL) {
dev_err(card->dev, "Cannot allocate PCM\n");
if (!pcm)
return -ENOMEM;
}
pcm->card = card;
pcm->device = device;
pcm->internal = internal;
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
INIT_LIST_HEAD(&pcm->list);
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id));
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
@ -792,8 +787,6 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
snd_pcm_free(pcm);
return err;
}
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
snd_pcm_free(pcm);
return err;
@ -888,8 +881,9 @@ static int snd_pcm_free(struct snd_pcm *pcm)
if (!pcm)
return 0;
list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_unregister(pcm);
if (!pcm->internal) {
list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_unregister(pcm);
}
if (pcm->private_free)
pcm->private_free(pcm);
@ -919,6 +913,9 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
if (snd_BUG_ON(!pcm || !rsubstream))
return -ENXIO;
if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK &&
stream != SNDRV_PCM_STREAM_CAPTURE))
return -EINVAL;
*rsubstream = NULL;
pstr = &pcm->streams[stream];
if (pstr->substream == NULL || pstr->substream_count == 0)
@ -927,25 +924,14 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
card = pcm->card;
prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM);
switch (stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) {
if (SUBSTREAM_BUSY(substream))
return -EAGAIN;
}
if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
int opposite = !stream;
for (substream = pcm->streams[opposite].substream; substream;
substream = substream->next) {
if (SUBSTREAM_BUSY(substream))
return -EAGAIN;
}
break;
case SNDRV_PCM_STREAM_CAPTURE:
if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) {
if (SUBSTREAM_BUSY(substream))
return -EAGAIN;
}
}
break;
default:
return -EINVAL;
}
if (file->f_flags & O_APPEND) {
@ -968,15 +954,12 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
return 0;
}
if (prefer_subdevice >= 0) {
for (substream = pstr->substream; substream; substream = substream->next)
if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice)
goto __ok;
}
for (substream = pstr->substream; substream; substream = substream->next)
if (!SUBSTREAM_BUSY(substream))
for (substream = pstr->substream; substream; substream = substream->next) {
if (!SUBSTREAM_BUSY(substream) &&
(prefer_subdevice == -1 ||
substream->number == prefer_subdevice))
break;
__ok:
}
if (substream == NULL)
return -EAGAIN;
@ -1086,15 +1069,16 @@ static int snd_pcm_dev_register(struct snd_device *device)
if (snd_BUG_ON(!device || !device->device_data))
return -ENXIO;
pcm = device->device_data;
if (pcm->internal)
return 0;
mutex_lock(&register_mutex);
err = snd_pcm_add(pcm);
if (err) {
mutex_unlock(&register_mutex);
return err;
}
if (err)
goto unlock;
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL || pcm->internal)
if (pcm->streams[cidx].substream == NULL)
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
@ -1109,9 +1093,8 @@ static int snd_pcm_dev_register(struct snd_device *device)
&snd_pcm_f_ops[cidx], pcm,
&pcm->streams[cidx].dev);
if (err < 0) {
list_del(&pcm->list);
mutex_unlock(&register_mutex);
return err;
list_del_init(&pcm->list);
goto unlock;
}
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
@ -1121,8 +1104,9 @@ static int snd_pcm_dev_register(struct snd_device *device)
list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_register(pcm);
unlock:
mutex_unlock(&register_mutex);
return 0;
return err;
}
static int snd_pcm_dev_disconnect(struct snd_device *device)
@ -1133,13 +1117,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
int cidx;
mutex_lock(&register_mutex);
if (list_empty(&pcm->list))
goto unlock;
mutex_lock(&pcm->open_mutex);
wake_up(&pcm->open_wait);
list_del_init(&pcm->list);
for (cidx = 0; cidx < 2; cidx++)
for (cidx = 0; cidx < 2; cidx++) {
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) {
snd_pcm_stream_lock_irq(substream);
if (substream->runtime) {
@ -1149,18 +1130,20 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
}
snd_pcm_stream_unlock_irq(substream);
}
list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_disconnect(pcm);
}
if (!pcm->internal) {
list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_disconnect(pcm);
}
for (cidx = 0; cidx < 2; cidx++) {
snd_unregister_device(&pcm->streams[cidx].dev);
if (!pcm->internal)
snd_unregister_device(&pcm->streams[cidx].dev);
if (pcm->streams[cidx].chmap_kctl) {
snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl);
pcm->streams[cidx].chmap_kctl = NULL;
}
}
mutex_unlock(&pcm->open_mutex);
unlock:
mutex_unlock(&register_mutex);
return 0;
}

View file

@ -194,18 +194,30 @@ struct snd_pcm_status32 {
u32 avail_max;
u32 overrange;
s32 suspended_state;
u32 reserved_alignment;
u32 audio_tstamp_data;
struct compat_timespec audio_tstamp;
unsigned char reserved[56-sizeof(struct compat_timespec)];
struct compat_timespec driver_tstamp;
u32 audio_tstamp_accuracy;
unsigned char reserved[52-2*sizeof(struct compat_timespec)];
} __attribute__((packed));
static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
struct snd_pcm_status32 __user *src)
struct snd_pcm_status32 __user *src,
bool ext)
{
struct snd_pcm_status status;
int err;
memset(&status, 0, sizeof(status));
/*
* with extension, parameters are read/write,
* get audio_tstamp_data from user,
* ignore rest of status structure
*/
if (ext && get_user(status.audio_tstamp_data,
(u32 __user *)(&src->audio_tstamp_data)))
return -EFAULT;
err = snd_pcm_status(substream, &status);
if (err < 0)
return err;
@ -222,7 +234,10 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
put_user(status.avail_max, &src->avail_max) ||
put_user(status.overrange, &src->overrange) ||
put_user(status.suspended_state, &src->suspended_state) ||
compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp))
put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
return -EFAULT;
return err;
@ -457,6 +472,7 @@ enum {
SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
@ -517,7 +533,9 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_SW_PARAMS32:
return snd_pcm_ioctl_sw_params_compat(substream, argp);
case SNDRV_PCM_IOCTL_STATUS32:
return snd_pcm_status_user_compat(substream, argp);
return snd_pcm_status_user_compat(substream, argp, false);
case SNDRV_PCM_IOCTL_STATUS_EXT32:
return snd_pcm_status_user_compat(substream, argp, true);
case SNDRV_PCM_IOCTL_SYNC_PTR32:
return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
case SNDRV_PCM_IOCTL_CHANNEL_INFO32:

View file

@ -289,7 +289,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
*
* The function should usually be called from the pcm open callback. Note that
* this function will use private_data field of the substream's runtime. So it
* is not availabe to your pcm driver implementation.
* is not available to your pcm driver implementation.
*/
int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
struct dma_chan *chan)
@ -328,7 +328,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
* This function will request a DMA channel using the passed filter function and
* data. The function should usually be called from the pcm open callback. Note
* that this function will use private_data field of the substream's runtime. So
* it is not availabe to your pcm driver implementation.
* it is not available to your pcm driver implementation.
*/
int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
dma_filter_fn filter_fn, void *filter_data)

View file

@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
return 0;
}
static void update_audio_tstamp(struct snd_pcm_substream *substream,
struct timespec *curr_tstamp,
struct timespec *audio_tstamp)
{
struct snd_pcm_runtime *runtime = substream->runtime;
u64 audio_frames, audio_nsecs;
struct timespec driver_tstamp;
if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE)
return;
if (!(substream->ops->get_time_info) ||
(runtime->audio_tstamp_report.actual_type ==
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
/*
* provide audio timestamp derived from pointer position
* add delay only if requested
*/
audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr;
if (runtime->audio_tstamp_config.report_delay) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
audio_frames -= runtime->delay;
else
audio_frames += runtime->delay;
}
audio_nsecs = div_u64(audio_frames * 1000000000LL,
runtime->rate);
*audio_tstamp = ns_to_timespec(audio_nsecs);
}
runtime->status->audio_tstamp = *audio_tstamp;
runtime->status->tstamp = *curr_tstamp;
/*
* re-take a driver timestamp to let apps detect if the reference tstamp
* read by low-level hardware was provided with a delay
*/
snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp);
runtime->driver_tstamp = driver_tstamp;
}
static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
unsigned int in_interrupt)
{
@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
pos = substream->ops->pointer(substream);
curr_jiffies = jiffies;
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
if ((substream->ops->get_time_info) &&
(runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
substream->ops->get_time_info(substream, &curr_tstamp,
&audio_tstamp,
&runtime->audio_tstamp_config,
&runtime->audio_tstamp_report);
if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) &&
(substream->ops->wall_clock))
substream->ops->wall_clock(substream, &audio_tstamp);
/* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */
if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
} else
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
}
if (pos == SNDRV_PCM_POS_XRUN) {
@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
no_delta_check:
if (runtime->status->hw_ptr == new_hw_ptr)
if (runtime->status->hw_ptr == new_hw_ptr) {
update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
return 0;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_BUG_ON(crossed_boundary != 1);
runtime->hw_ptr_wrap += runtime->boundary;
}
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
runtime->status->tstamp = curr_tstamp;
if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) {
/*
* no wall clock available, provide audio timestamp
* derived from pointer position+delay
*/
u64 audio_frames, audio_nsecs;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
audio_frames = runtime->hw_ptr_wrap
+ runtime->status->hw_ptr
- runtime->delay;
else
audio_frames = runtime->hw_ptr_wrap
+ runtime->status->hw_ptr
+ runtime->delay;
audio_nsecs = div_u64(audio_frames * 1000000000LL,
runtime->rate);
audio_tstamp = ns_to_timespec(audio_nsecs);
}
runtime->status->audio_tstamp = audio_tstamp;
}
update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
return snd_pcm_update_state(substream, runtime);
}

View file

@ -707,6 +707,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_stream_lock_irq(substream);
snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data,
&runtime->audio_tstamp_config);
/* backwards compatible behavior */
if (runtime->audio_tstamp_config.type_requested ==
SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT) {
if (runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)
runtime->audio_tstamp_config.type_requested =
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
else
runtime->audio_tstamp_config.type_requested =
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
runtime->audio_tstamp_report.valid = 0;
} else
runtime->audio_tstamp_report.valid = 1;
status->state = runtime->status->state;
status->suspended_state = runtime->status->suspended_state;
if (status->state == SNDRV_PCM_STATE_OPEN)
@ -716,8 +733,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
snd_pcm_update_hw_ptr(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
status->tstamp = runtime->status->tstamp;
status->driver_tstamp = runtime->driver_tstamp;
status->audio_tstamp =
runtime->status->audio_tstamp;
if (runtime->audio_tstamp_report.valid == 1)
/* backwards compatibility, no report provided in COMPAT mode */
snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data,
&status->audio_tstamp_accuracy,
&runtime->audio_tstamp_report);
goto _tstamp_end;
}
} else {
@ -753,12 +777,21 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
}
static int snd_pcm_status_user(struct snd_pcm_substream *substream,
struct snd_pcm_status __user * _status)
struct snd_pcm_status __user * _status,
bool ext)
{
struct snd_pcm_status status;
int res;
memset(&status, 0, sizeof(status));
/*
* with extension, parameters are read/write,
* get audio_tstamp_data from user,
* ignore rest of status structure
*/
if (ext && get_user(status.audio_tstamp_data,
(u32 __user *)(&_status->audio_tstamp_data)))
return -EFAULT;
res = snd_pcm_status(substream, &status);
if (res < 0)
return res;
@ -2725,7 +2758,9 @@ static int snd_pcm_common_ioctl1(struct file *file,
case SNDRV_PCM_IOCTL_SW_PARAMS:
return snd_pcm_sw_params_user(substream, arg);
case SNDRV_PCM_IOCTL_STATUS:
return snd_pcm_status_user(substream, arg);
return snd_pcm_status_user(substream, arg, false);
case SNDRV_PCM_IOCTL_STATUS_EXT:
return snd_pcm_status_user(substream, arg, true);
case SNDRV_PCM_IOCTL_CHANNEL_INFO:
return snd_pcm_channel_info_user(substream, arg);
case SNDRV_PCM_IOCTL_PREPARE:

View file

@ -1429,10 +1429,8 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
for (idx = 0; idx < count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
if (substream == NULL) {
rmidi_err(rmidi, "rawmidi: cannot allocate substream\n");
if (!substream)
return -ENOMEM;
}
substream->stream = direction;
substream->number = idx;
substream->rmidi = rmidi;
@ -1479,10 +1477,8 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
if (rrawmidi)
*rrawmidi = NULL;
rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
if (rmidi == NULL) {
dev_err(card->dev, "rawmidi: cannot allocate\n");
if (!rmidi)
return -ENOMEM;
}
rmidi->card = card;
rmidi->device = device;
mutex_init(&rmidi->open_mutex);

View file

@ -65,15 +65,20 @@ static unsigned int odev_poll(struct file *file, poll_table * wait);
* module interface
*/
static struct snd_seq_driver seq_oss_synth_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_seq_oss_synth_probe,
.remove = snd_seq_oss_synth_remove,
},
.id = SNDRV_SEQ_DEV_ID_OSS,
.argsize = sizeof(struct snd_seq_oss_reg),
};
static int __init alsa_seq_oss_init(void)
{
int rc;
static struct snd_seq_dev_ops ops = {
snd_seq_oss_synth_register,
snd_seq_oss_synth_unregister,
};
snd_seq_autoload_lock();
if ((rc = register_device()) < 0)
goto error;
if ((rc = register_proc()) < 0) {
@ -86,8 +91,8 @@ static int __init alsa_seq_oss_init(void)
goto error;
}
if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
sizeof(struct snd_seq_oss_reg))) < 0) {
rc = snd_seq_driver_register(&seq_oss_synth_driver);
if (rc < 0) {
snd_seq_oss_delete_client();
unregister_proc();
unregister_device();
@ -98,13 +103,12 @@ static int __init alsa_seq_oss_init(void)
snd_seq_oss_synth_init();
error:
snd_seq_autoload_unlock();
return rc;
}
static void __exit alsa_seq_oss_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
snd_seq_driver_unregister(&seq_oss_synth_driver);
snd_seq_oss_delete_client();
unregister_proc();
unregister_device();

View file

@ -188,10 +188,8 @@ snd_seq_oss_open(struct file *file, int level)
struct seq_oss_devinfo *dp;
dp = kzalloc(sizeof(*dp), GFP_KERNEL);
if (!dp) {
pr_err("ALSA: seq_oss: can't malloc device info\n");
if (!dp)
return -ENOMEM;
}
dp->cseq = system_client;
dp->port = -1;

View file

@ -173,10 +173,9 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
/*
* allocate midi info record
*/
if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) {
pr_err("ALSA: seq_oss: can't malloc midi info\n");
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
if (!mdev)
return -ENOMEM;
}
/* copy the port information */
mdev->client = pinfo->addr.client;

View file

@ -47,13 +47,12 @@ snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen)
{
struct seq_oss_readq *q;
if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL) {
pr_err("ALSA: seq_oss: can't malloc read queue\n");
q = kzalloc(sizeof(*q), GFP_KERNEL);
if (!q)
return NULL;
}
if ((q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL)) == NULL) {
pr_err("ALSA: seq_oss: can't malloc read queue buffer\n");
q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL);
if (!q->q) {
kfree(q);
return NULL;
}

View file

@ -98,17 +98,17 @@ snd_seq_oss_synth_init(void)
* registration of the synth device
*/
int
snd_seq_oss_synth_register(struct snd_seq_device *dev)
snd_seq_oss_synth_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
int i;
struct seq_oss_synth *rec;
struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
unsigned long flags;
if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL) {
pr_err("ALSA: seq_oss: can't malloc synth info\n");
rec = kzalloc(sizeof(*rec), GFP_KERNEL);
if (!rec)
return -ENOMEM;
}
rec->seq_device = -1;
rec->synth_type = reg->type;
rec->synth_subtype = reg->subtype;
@ -149,8 +149,9 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
int
snd_seq_oss_synth_unregister(struct snd_seq_device *dev)
snd_seq_oss_synth_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
int index;
struct seq_oss_synth *rec = dev->driver_data;
unsigned long flags;
@ -247,7 +248,6 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
if (info->nr_voices > 0) {
info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);
if (!info->ch) {
pr_err("ALSA: seq_oss: Cannot malloc voices\n");
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
snd_use_lock_free(&rec->use_lock);

View file

@ -28,8 +28,8 @@
#include <sound/seq_device.h>
void snd_seq_oss_synth_init(void);
int snd_seq_oss_synth_register(struct snd_seq_device *dev);
int snd_seq_oss_synth_unregister(struct snd_seq_device *dev);
int snd_seq_oss_synth_probe(struct device *dev);
int snd_seq_oss_synth_remove(struct device *dev);
void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp);

View file

@ -36,6 +36,7 @@
*
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <sound/core.h>
@ -51,140 +52,78 @@ MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ALSA sequencer device management");
MODULE_LICENSE("GPL");
/* driver state */
#define DRIVER_EMPTY 0
#define DRIVER_LOADED (1<<0)
#define DRIVER_REQUESTED (1<<1)
#define DRIVER_LOCKED (1<<2)
#define DRIVER_REQUESTING (1<<3)
/*
* bus definition
*/
static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
struct snd_seq_driver *sdrv = to_seq_drv(drv);
struct ops_list {
char id[ID_LEN]; /* driver id */
int driver; /* driver state */
int used; /* reference counter */
int argsize; /* argument size */
return strcmp(sdrv->id, sdev->id) == 0 &&
sdrv->argsize == sdev->argsize;
}
/* operators */
struct snd_seq_dev_ops ops;
/* registered devices */
struct list_head dev_list; /* list of devices */
int num_devices; /* number of associated devices */
int num_init_devices; /* number of initialized devices */
struct mutex reg_mutex;
struct list_head list; /* next driver */
static struct bus_type snd_seq_bus_type = {
.name = "snd_seq",
.match = snd_seq_bus_match,
};
static LIST_HEAD(opslist);
static int num_ops;
static DEFINE_MUTEX(ops_mutex);
/*
* proc interface -- just for compatibility
*/
#ifdef CONFIG_PROC_FS
static struct snd_info_entry *info_entry;
#endif
/*
* prototypes
*/
static int snd_seq_device_free(struct snd_seq_device *dev);
static int snd_seq_device_dev_free(struct snd_device *device);
static int snd_seq_device_dev_register(struct snd_device *device);
static int snd_seq_device_dev_disconnect(struct snd_device *device);
static int print_dev_info(struct device *dev, void *data)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
struct snd_info_buffer *buffer = data;
static int init_device(struct snd_seq_device *dev, struct ops_list *ops);
static int free_device(struct snd_seq_device *dev, struct ops_list *ops);
static struct ops_list *find_driver(char *id, int create_if_empty);
static struct ops_list *create_driver(char *id);
static void unlock_driver(struct ops_list *ops);
static void remove_drivers(void);
snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
dev->driver ? "loaded" : "empty",
dev->driver ? 1 : 0);
return 0;
}
/*
* show all drivers and their status
*/
#ifdef CONFIG_PROC_FS
static void snd_seq_device_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list) {
snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
ops->id,
ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
ops->driver & DRIVER_REQUESTED ? ",requested" : "",
ops->driver & DRIVER_LOCKED ? ",locked" : "",
ops->num_devices);
}
mutex_unlock(&ops_mutex);
bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
}
#endif
/*
* load all registered drivers (called from seq_clientmgr.c)
*/
#ifdef CONFIG_MODULES
/* avoid auto-loading during module_init() */
/* flag to block auto-loading */
static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
void snd_seq_autoload_lock(void)
static int request_seq_drv(struct device *dev, void *data)
{
atomic_inc(&snd_seq_in_init);
struct snd_seq_device *sdev = to_seq_dev(dev);
if (!dev->driver)
request_module("snd-%s", sdev->id);
return 0;
}
void snd_seq_autoload_unlock(void)
{
atomic_dec(&snd_seq_in_init);
}
static void autoload_drivers(void)
static void autoload_drivers(struct work_struct *work)
{
/* avoid reentrance */
if (atomic_inc_return(&snd_seq_in_init) == 1) {
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list) {
if ((ops->driver & DRIVER_REQUESTING) &&
!(ops->driver & DRIVER_REQUESTED)) {
ops->used++;
mutex_unlock(&ops_mutex);
ops->driver |= DRIVER_REQUESTED;
request_module("snd-%s", ops->id);
mutex_lock(&ops_mutex);
ops->used--;
}
}
mutex_unlock(&ops_mutex);
}
if (atomic_inc_return(&snd_seq_in_init) == 1)
bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
request_seq_drv);
atomic_dec(&snd_seq_in_init);
}
static void call_autoload(struct work_struct *work)
{
autoload_drivers();
}
static DECLARE_WORK(autoload_work, call_autoload);
static void try_autoload(struct ops_list *ops)
{
if (!ops->driver) {
ops->driver |= DRIVER_REQUESTING;
schedule_work(&autoload_work);
}
}
static DECLARE_WORK(autoload_work, autoload_drivers);
static void queue_autoload_drivers(void)
{
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list)
try_autoload(ops);
mutex_unlock(&ops_mutex);
schedule_work(&autoload_work);
}
void snd_seq_autoload_init(void)
@ -195,16 +134,63 @@ void snd_seq_autoload_init(void)
queue_autoload_drivers();
#endif
}
#else
#define try_autoload(ops) /* NOP */
#endif
EXPORT_SYMBOL(snd_seq_autoload_init);
void snd_seq_autoload_exit(void)
{
atomic_inc(&snd_seq_in_init);
}
EXPORT_SYMBOL(snd_seq_autoload_exit);
void snd_seq_device_load_drivers(void)
{
#ifdef CONFIG_MODULES
queue_autoload_drivers();
flush_work(&autoload_work);
}
EXPORT_SYMBOL(snd_seq_device_load_drivers);
#else
#define queue_autoload_drivers() /* NOP */
#endif
/*
* device management
*/
static int snd_seq_device_dev_free(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
put_device(&dev->dev);
return 0;
}
static int snd_seq_device_dev_register(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
int err;
err = device_add(&dev->dev);
if (err < 0)
return err;
if (!dev->dev.driver)
queue_autoload_drivers();
return 0;
}
static int snd_seq_device_dev_disconnect(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
device_del(&dev->dev);
return 0;
}
static void snd_seq_dev_release(struct device *dev)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
if (sdev->private_free)
sdev->private_free(sdev);
kfree(sdev);
}
/*
@ -214,11 +200,10 @@ void snd_seq_device_load_drivers(void)
* id = id of driver
* result = return pointer (NULL allowed if unnecessary)
*/
int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
struct snd_seq_device **result)
int snd_seq_device_new(struct snd_card *card, int device, const char *id,
int argsize, struct snd_seq_device **result)
{
struct snd_seq_device *dev;
struct ops_list *ops;
int err;
static struct snd_device_ops dops = {
.dev_free = snd_seq_device_dev_free,
@ -232,347 +217,60 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
if (snd_BUG_ON(!id))
return -EINVAL;
ops = find_driver(id, 1);
if (ops == NULL)
dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev = kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL);
if (dev == NULL) {
unlock_driver(ops);
return -ENOMEM;
}
/* set up device info */
dev->card = card;
dev->device = device;
strlcpy(dev->id, id, sizeof(dev->id));
dev->id = id;
dev->argsize = argsize;
dev->status = SNDRV_SEQ_DEVICE_FREE;
device_initialize(&dev->dev);
dev->dev.parent = &card->card_dev;
dev->dev.bus = &snd_seq_bus_type;
dev->dev.release = snd_seq_dev_release;
dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
/* add this device to the list */
mutex_lock(&ops->reg_mutex);
list_add_tail(&dev->list, &ops->dev_list);
ops->num_devices++;
mutex_unlock(&ops->reg_mutex);
if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
snd_seq_device_free(dev);
err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
if (err < 0) {
put_device(&dev->dev);
return err;
}
try_autoload(ops);
unlock_driver(ops);
if (result)
*result = dev;
return 0;
}
EXPORT_SYMBOL(snd_seq_device_new);
/*
* free the existing device
* driver registration
*/
static int snd_seq_device_free(struct snd_seq_device *dev)
int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
{
struct ops_list *ops;
if (snd_BUG_ON(!dev))
if (WARN_ON(!drv->driver.name || !drv->id))
return -EINVAL;
ops = find_driver(dev->id, 0);
if (ops == NULL)
return -ENXIO;
/* remove the device from the list */
mutex_lock(&ops->reg_mutex);
list_del(&dev->list);
ops->num_devices--;
mutex_unlock(&ops->reg_mutex);
free_device(dev, ops);
if (dev->private_free)
dev->private_free(dev);
kfree(dev);
unlock_driver(ops);
return 0;
drv->driver.bus = &snd_seq_bus_type;
drv->driver.owner = mod;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
static int snd_seq_device_dev_free(struct snd_device *device)
void snd_seq_driver_unregister(struct snd_seq_driver *drv)
{
struct snd_seq_device *dev = device->device_data;
return snd_seq_device_free(dev);
driver_unregister(&drv->driver);
}
/*
* register the device
*/
static int snd_seq_device_dev_register(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
struct ops_list *ops;
ops = find_driver(dev->id, 0);
if (ops == NULL)
return -ENOENT;
/* initialize this device if the corresponding driver was
* already loaded
*/
if (ops->driver & DRIVER_LOADED)
init_device(dev, ops);
unlock_driver(ops);
return 0;
}
/*
* disconnect the device
*/
static int snd_seq_device_dev_disconnect(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
struct ops_list *ops;
ops = find_driver(dev->id, 0);
if (ops == NULL)
return -ENOENT;
free_device(dev, ops);
unlock_driver(ops);
return 0;
}
/*
* register device driver
* id = driver id
* entry = driver operators - duplicated to each instance
*/
int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
int argsize)
{
struct ops_list *ops;
struct snd_seq_device *dev;
if (id == NULL || entry == NULL ||
entry->init_device == NULL || entry->free_device == NULL)
return -EINVAL;
ops = find_driver(id, 1);
if (ops == NULL)
return -ENOMEM;
if (ops->driver & DRIVER_LOADED) {
pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id);
unlock_driver(ops);
return -EBUSY;
}
mutex_lock(&ops->reg_mutex);
/* copy driver operators */
ops->ops = *entry;
ops->driver |= DRIVER_LOADED;
ops->argsize = argsize;
/* initialize existing devices if necessary */
list_for_each_entry(dev, &ops->dev_list, list) {
init_device(dev, ops);
}
mutex_unlock(&ops->reg_mutex);
unlock_driver(ops);
return 0;
}
/*
* create driver record
*/
static struct ops_list * create_driver(char *id)
{
struct ops_list *ops;
ops = kzalloc(sizeof(*ops), GFP_KERNEL);
if (ops == NULL)
return ops;
/* set up driver entry */
strlcpy(ops->id, id, sizeof(ops->id));
mutex_init(&ops->reg_mutex);
/*
* The ->reg_mutex locking rules are per-driver, so we create
* separate per-driver lock classes:
*/
lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id);
ops->driver = DRIVER_EMPTY;
INIT_LIST_HEAD(&ops->dev_list);
/* lock this instance */
ops->used = 1;
/* register driver entry */
mutex_lock(&ops_mutex);
list_add_tail(&ops->list, &opslist);
num_ops++;
mutex_unlock(&ops_mutex);
return ops;
}
/*
* unregister the specified driver
*/
int snd_seq_device_unregister_driver(char *id)
{
struct ops_list *ops;
struct snd_seq_device *dev;
ops = find_driver(id, 0);
if (ops == NULL)
return -ENXIO;
if (! (ops->driver & DRIVER_LOADED) ||
(ops->driver & DRIVER_LOCKED)) {
pr_err("ALSA: seq: driver_unregister: cannot unload driver '%s': status=%x\n",
id, ops->driver);
unlock_driver(ops);
return -EBUSY;
}
/* close and release all devices associated with this driver */
mutex_lock(&ops->reg_mutex);
ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
list_for_each_entry(dev, &ops->dev_list, list) {
free_device(dev, ops);
}
ops->driver = 0;
if (ops->num_init_devices > 0)
pr_err("ALSA: seq: free_driver: init_devices > 0!! (%d)\n",
ops->num_init_devices);
mutex_unlock(&ops->reg_mutex);
unlock_driver(ops);
/* remove empty driver entries */
remove_drivers();
return 0;
}
/*
* remove empty driver entries
*/
static void remove_drivers(void)
{
struct list_head *head;
mutex_lock(&ops_mutex);
head = opslist.next;
while (head != &opslist) {
struct ops_list *ops = list_entry(head, struct ops_list, list);
if (! (ops->driver & DRIVER_LOADED) &&
ops->used == 0 && ops->num_devices == 0) {
head = head->next;
list_del(&ops->list);
kfree(ops);
num_ops--;
} else
head = head->next;
}
mutex_unlock(&ops_mutex);
}
/*
* initialize the device - call init_device operator
*/
static int init_device(struct snd_seq_device *dev, struct ops_list *ops)
{
if (! (ops->driver & DRIVER_LOADED))
return 0; /* driver is not loaded yet */
if (dev->status != SNDRV_SEQ_DEVICE_FREE)
return 0; /* already initialized */
if (ops->argsize != dev->argsize) {
pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n",
dev->name, ops->id, ops->argsize, dev->argsize);
return -EINVAL;
}
if (ops->ops.init_device(dev) >= 0) {
dev->status = SNDRV_SEQ_DEVICE_REGISTERED;
ops->num_init_devices++;
} else {
pr_err("ALSA: seq: init_device failed: %s: %s\n",
dev->name, dev->id);
}
return 0;
}
/*
* release the device - call free_device operator
*/
static int free_device(struct snd_seq_device *dev, struct ops_list *ops)
{
int result;
if (! (ops->driver & DRIVER_LOADED))
return 0; /* driver is not loaded yet */
if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED)
return 0; /* not registered */
if (ops->argsize != dev->argsize) {
pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n",
dev->name, ops->id, ops->argsize, dev->argsize);
return -EINVAL;
}
if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) {
dev->status = SNDRV_SEQ_DEVICE_FREE;
dev->driver_data = NULL;
ops->num_init_devices--;
} else {
pr_err("ALSA: seq: free_device failed: %s: %s\n",
dev->name, dev->id);
}
return 0;
}
/*
* find the matching driver with given id
*/
static struct ops_list * find_driver(char *id, int create_if_empty)
{
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list) {
if (strcmp(ops->id, id) == 0) {
ops->used++;
mutex_unlock(&ops_mutex);
return ops;
}
}
mutex_unlock(&ops_mutex);
if (create_if_empty)
return create_driver(id);
return NULL;
}
static void unlock_driver(struct ops_list *ops)
{
mutex_lock(&ops_mutex);
ops->used--;
mutex_unlock(&ops_mutex);
}
EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
/*
* module part
*/
static int __init alsa_seq_device_init(void)
static int __init seq_dev_proc_init(void)
{
#ifdef CONFIG_PROC_FS
info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
@ -589,28 +287,29 @@ static int __init alsa_seq_device_init(void)
return 0;
}
static int __init alsa_seq_device_init(void)
{
int err;
err = bus_register(&snd_seq_bus_type);
if (err < 0)
return err;
err = seq_dev_proc_init();
if (err < 0)
bus_unregister(&snd_seq_bus_type);
return err;
}
static void __exit alsa_seq_device_exit(void)
{
#ifdef CONFIG_MODULES
cancel_work_sync(&autoload_work);
#endif
remove_drivers();
#ifdef CONFIG_PROC_FS
snd_info_free_entry(info_entry);
#endif
if (num_ops)
pr_err("ALSA: seq: drivers not released (%d)\n", num_ops);
bus_unregister(&snd_seq_bus_type);
}
module_init(alsa_seq_device_init)
subsys_initcall(alsa_seq_device_init)
module_exit(alsa_seq_device_exit)
EXPORT_SYMBOL(snd_seq_device_load_drivers);
EXPORT_SYMBOL(snd_seq_device_new);
EXPORT_SYMBOL(snd_seq_device_register_driver);
EXPORT_SYMBOL(snd_seq_device_unregister_driver);
#ifdef CONFIG_MODULES
EXPORT_SYMBOL(snd_seq_autoload_init);
EXPORT_SYMBOL(snd_seq_autoload_lock);
EXPORT_SYMBOL(snd_seq_autoload_unlock);
#endif

View file

@ -214,11 +214,7 @@ delete_client(void)
static int __init alsa_seq_dummy_init(void)
{
int err;
snd_seq_autoload_lock();
err = register_client();
snd_seq_autoload_unlock();
return err;
return register_client();
}
static void __exit alsa_seq_dummy_exit(void)

View file

@ -33,10 +33,8 @@ struct snd_seq_fifo *snd_seq_fifo_new(int poolsize)
struct snd_seq_fifo *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
if (f == NULL) {
pr_debug("ALSA: seq: malloc failed for snd_seq_fifo_new() \n");
if (!f)
return NULL;
}
f->pool = snd_seq_pool_new(poolsize);
if (f->pool == NULL) {

View file

@ -387,10 +387,8 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
return 0;
pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
if (pool->ptr == NULL) {
pr_debug("ALSA: seq: malloc for sequencer events failed\n");
if (!pool->ptr)
return -ENOMEM;
}
/* add new cells to the free cell list */
spin_lock_irqsave(&pool->lock, flags);
@ -463,10 +461,8 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize)
/* create pool block */
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
if (pool == NULL) {
pr_debug("ALSA: seq: malloc failed for pool\n");
if (!pool)
return NULL;
}
spin_lock_init(&pool->lock);
pool->ptr = NULL;
pool->free = NULL;

View file

@ -273,8 +273,9 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
/* register new midi synth port */
static int
snd_seq_midisynth_register_port(struct snd_seq_device *dev)
snd_seq_midisynth_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct seq_midisynth_client *client;
struct seq_midisynth *msynth, *ms;
struct snd_seq_port_info *port;
@ -427,8 +428,9 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
/* release midi synth port */
static int
snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
snd_seq_midisynth_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct seq_midisynth_client *client;
struct seq_midisynth *msynth;
struct snd_card *card = dev->card;
@ -457,24 +459,14 @@ snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
return 0;
}
static struct snd_seq_driver seq_midisynth_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_seq_midisynth_probe,
.remove = snd_seq_midisynth_remove,
},
.id = SNDRV_SEQ_DEV_ID_MIDISYNTH,
.argsize = 0,
};
static int __init alsa_seq_midi_init(void)
{
static struct snd_seq_dev_ops ops = {
snd_seq_midisynth_register_port,
snd_seq_midisynth_unregister_port,
};
memset(&synths, 0, sizeof(synths));
snd_seq_autoload_lock();
snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
snd_seq_autoload_unlock();
return 0;
}
static void __exit alsa_seq_midi_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
}
module_init(alsa_seq_midi_init)
module_exit(alsa_seq_midi_exit)
module_snd_seq_driver(seq_midisynth_driver);

View file

@ -141,10 +141,8 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
/* create a new port */
new_port = kzalloc(sizeof(*new_port), GFP_KERNEL);
if (! new_port) {
pr_debug("ALSA: seq: malloc failed for registering client port\n");
if (!new_port)
return NULL; /* failure, out of memory */
}
/* init port data */
new_port->addr.client = client->number;
new_port->addr.port = -1;

View file

@ -59,10 +59,8 @@ struct snd_seq_prioq *snd_seq_prioq_new(void)
struct snd_seq_prioq *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
if (f == NULL) {
pr_debug("ALSA: seq: malloc failed for snd_seq_prioq_new()\n");
if (!f)
return NULL;
}
spin_lock_init(&f->lock);
f->head = NULL;

View file

@ -111,10 +111,8 @@ static struct snd_seq_queue *queue_new(int owner, int locked)
struct snd_seq_queue *q;
q = kzalloc(sizeof(*q), GFP_KERNEL);
if (q == NULL) {
pr_debug("ALSA: seq: malloc failed for snd_seq_queue_new()\n");
if (!q)
return NULL;
}
spin_lock_init(&q->owner_lock);
spin_lock_init(&q->check_lock);

View file

@ -56,10 +56,8 @@ struct snd_seq_timer *snd_seq_timer_new(void)
struct snd_seq_timer *tmr;
tmr = kzalloc(sizeof(*tmr), GFP_KERNEL);
if (tmr == NULL) {
pr_debug("ALSA: seq: malloc failed for snd_seq_timer_new() \n");
if (!tmr)
return NULL;
}
spin_lock_init(&tmr->lock);
/* reset setup to defaults */

View file

@ -186,7 +186,7 @@ static const struct file_operations snd_fops =
};
#ifdef CONFIG_SND_DYNAMIC_MINORS
static int snd_find_free_minor(int type)
static int snd_find_free_minor(int type, struct snd_card *card, int dev)
{
int minor;
@ -209,7 +209,7 @@ static int snd_find_free_minor(int type)
return -EBUSY;
}
#else
static int snd_kernel_minor(int type, struct snd_card *card, int dev)
static int snd_find_free_minor(int type, struct snd_card *card, int dev)
{
int minor;
@ -237,6 +237,8 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)
}
if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
return -EINVAL;
if (snd_minors[minor])
return -EBUSY;
return minor;
}
#endif
@ -276,13 +278,7 @@ int snd_register_device(int type, struct snd_card *card, int dev,
preg->private_data = private_data;
preg->card_ptr = card;
mutex_lock(&sound_mutex);
#ifdef CONFIG_SND_DYNAMIC_MINORS
minor = snd_find_free_minor(type);
#else
minor = snd_kernel_minor(type, card, dev);
if (minor >= 0 && snd_minors[minor])
minor = -EBUSY;
#endif
minor = snd_find_free_minor(type, card, dev);
if (minor < 0) {
err = minor;
goto error;

View file

@ -774,10 +774,8 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
if (rtimer)
*rtimer = NULL;
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
if (timer == NULL) {
pr_err("ALSA: timer: cannot allocate\n");
if (!timer)
return -ENOMEM;
}
timer->tmr_class = tid->dev_class;
timer->card = card;
timer->tmr_device = tid->device;

View file

@ -216,8 +216,9 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
/* ------------------------------ */
static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
static int snd_opl3_seq_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl3 *opl3;
int client, err;
char name[32];
@ -257,8 +258,9 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
return 0;
}
static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
static int snd_opl3_seq_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl3 *opl3;
opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
@ -275,22 +277,14 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
return 0;
}
static int __init alsa_opl3_seq_init(void)
{
static struct snd_seq_dev_ops ops =
{
snd_opl3_seq_new_device,
snd_opl3_seq_delete_device
};
static struct snd_seq_driver opl3_seq_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_opl3_seq_probe,
.remove = snd_opl3_seq_remove,
},
.id = SNDRV_SEQ_DEV_ID_OPL3,
.argsize = sizeof(struct snd_opl3 *),
};
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops,
sizeof(struct snd_opl3 *));
}
static void __exit alsa_opl3_seq_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3);
}
module_init(alsa_opl3_seq_init)
module_exit(alsa_opl3_seq_exit)
module_snd_seq_driver(opl3_seq_driver);

View file

@ -124,8 +124,9 @@ static void snd_opl4_seq_free_port(void *private_data)
snd_midi_channel_free_set(opl4->chset);
}
static int snd_opl4_seq_new_device(struct snd_seq_device *dev)
static int snd_opl4_seq_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl4 *opl4;
int client;
struct snd_seq_port_callback pcallbacks;
@ -180,8 +181,9 @@ static int snd_opl4_seq_new_device(struct snd_seq_device *dev)
return 0;
}
static int snd_opl4_seq_delete_device(struct snd_seq_device *dev)
static int snd_opl4_seq_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl4 *opl4;
opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
@ -195,21 +197,14 @@ static int snd_opl4_seq_delete_device(struct snd_seq_device *dev)
return 0;
}
static int __init alsa_opl4_synth_init(void)
{
static struct snd_seq_dev_ops ops = {
snd_opl4_seq_new_device,
snd_opl4_seq_delete_device
};
static struct snd_seq_driver opl4_seq_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_opl4_seq_probe,
.remove = snd_opl4_seq_remove,
},
.id = SNDRV_SEQ_DEV_ID_OPL4,
.argsize = sizeof(struct snd_opl4 *),
};
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops,
sizeof(struct snd_opl4 *));
}
static void __exit alsa_opl4_synth_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4);
}
module_init(alsa_opl4_synth_init)
module_exit(alsa_opl4_synth_exit)
module_snd_seq_driver(opl4_seq_driver);

View file

@ -166,10 +166,10 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
* One AMDTP packet can include some frames. In blocking mode, the
* number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
* depending on its sampling rate. For accurate period interrupt, it's
* preferrable to aligh period/buffer sizes to current SYT_INTERVAL.
* preferrable to align period/buffer sizes to current SYT_INTERVAL.
*
* TODO: These constraints can be improved with propper rules.
* Currently apply LCM of SYT_INTEVALs.
* TODO: These constraints can be improved with proper rules.
* Currently apply LCM of SYT_INTERVALs.
*/
err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
@ -270,7 +270,7 @@ static void amdtp_read_s32(struct amdtp_stream *s,
* @s: the AMDTP stream to configure
* @format: the format of the ALSA PCM device
*
* The sample format must be set after the other paramters (rate/PCM channels/
* The sample format must be set after the other parameters (rate/PCM channels/
* MIDI) and before the stream is started, and must not be changed while the
* stream is running.
*/

View file

@ -13,7 +13,7 @@
*
* Transaction substance:
* At first, 6 data exist. Following to the data, parameters for each command
* exist. All of the parameters are 32 bit alighed to big endian.
* exist. All of the parameters are 32 bit aligned to big endian.
* data[0]: Length of transaction substance
* data[1]: Transaction version
* data[2]: Sequence number. This is incremented by the device

View file

@ -34,8 +34,9 @@ MODULE_LICENSE("GPL");
/*
* create a new hardware dependent device for Emu8000
*/
static int snd_emu8000_new_device(struct snd_seq_device *dev)
static int snd_emu8000_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emu8000 *hw;
struct snd_emux *emu;
@ -93,8 +94,9 @@ static int snd_emu8000_new_device(struct snd_seq_device *dev)
/*
* free all resources
*/
static int snd_emu8000_delete_device(struct snd_seq_device *dev)
static int snd_emu8000_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emu8000 *hw;
if (dev->driver_data == NULL)
@ -114,21 +116,14 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev)
* INIT part
*/
static int __init alsa_emu8000_init(void)
{
static struct snd_seq_dev_ops ops = {
snd_emu8000_new_device,
snd_emu8000_delete_device,
};
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops,
sizeof(struct snd_emu8000*));
}
static struct snd_seq_driver emu8000_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_emu8000_probe,
.remove = snd_emu8000_remove,
},
.id = SNDRV_SEQ_DEV_ID_EMU8000,
.argsize = sizeof(struct snd_emu8000 *),
};
static void __exit alsa_emu8000_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000);
}
module_init(alsa_emu8000_init)
module_exit(alsa_emu8000_exit)
module_snd_seq_driver(emu8000_driver);

View file

@ -666,7 +666,7 @@ static int opl3_start_note (int dev, int voice, int note, int volume)
opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
devc->voc[voice].keyon_byte = data;
devc->voc[voice].keyon_byte = data;
opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
if (voice_mode == 4)
opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
@ -717,7 +717,7 @@ static void freq_to_fnum (int freq, int *block, int *fnum)
static void opl3_command (int io_addr, unsigned int addr, unsigned int val)
{
int i;
int i;
/*
* The original 2-OP synth requires a quite long delay after writing to a

View file

@ -604,7 +604,7 @@ static void ess_audio_output_block_audio2
ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */
devc->irq_mode_16 = IMODE_OUTPUT;
devc->intr_active_16 = 1;
devc->intr_active_16 = 1;
}
static void ess_audio_output_block
@ -1183,17 +1183,12 @@ FKS_test (devc);
chip = "ES1688";
}
printk ( KERN_INFO "ESS chip %s %s%s\n"
, chip
, ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20
? "detected"
: "specified"
)
, ( devc->sbmo.esstype == ESSTYPE_LIKE20
? " (kernel 2.0 compatible)"
: ""
)
);
printk(KERN_INFO "ESS chip %s %s%s\n", chip,
(devc->sbmo.esstype == ESSTYPE_DETECT ||
devc->sbmo.esstype == ESSTYPE_LIKE20) ?
"detected" : "specified",
devc->sbmo.esstype == ESSTYPE_LIKE20 ?
" (kernel 2.0 compatible)" : "");
sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f);
} else {

View file

@ -179,14 +179,14 @@ void sb_dsp_midi_init(sb_devc * devc, struct module *owner)
{
printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n");
sound_unload_mididev(dev);
return;
return;
}
memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations,
sizeof(struct midi_operations));
if (owner)
midi_devs[dev]->owner = owner;
midi_devs[dev]->owner = owner;
midi_devs[dev]->devc = devc;

View file

@ -50,29 +50,24 @@ tmr2ticks(int tmr_value)
static void
poll_def_tmr(unsigned long dummy)
{
if (!opened)
return;
def_tmr.expires = (1) + jiffies;
add_timer(&def_tmr);
if (opened)
{
if (!tmr_running)
return;
{
def_tmr.expires = (1) + jiffies;
add_timer(&def_tmr);
}
spin_lock(&lock);
tmr_ctr++;
curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
if (tmr_running)
{
spin_lock(&lock);
tmr_ctr++;
curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
if (curr_ticks >= next_event_time) {
next_event_time = (unsigned long) -1;
sequencer_timer(0);
}
if (curr_ticks >= next_event_time)
{
next_event_time = (unsigned long) -1;
sequencer_timer(0);
}
spin_unlock(&lock);
}
}
spin_unlock(&lock);
}
static void

View file

@ -177,6 +177,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
{ 0x54584e03, 0xffffffff, "TLV320AIC27", NULL, NULL },
{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL },
{ 0x56494120, 0xfffffff0, "VIA1613", patch_vt1613, NULL },
{ 0x56494161, 0xffffffff, "VIA1612A", NULL, NULL }, // modified ICE1232 with S/PDIF
{ 0x56494170, 0xffffffff, "VIA1617A", patch_vt1617a, NULL }, // modified VT1616 with S/PDIF
{ 0x56494182, 0xffffffff, "VIA1618", patch_vt1618, NULL },

View file

@ -3351,6 +3351,33 @@ static int patch_cm9780(struct snd_ac97 *ac97)
return 0;
}
/*
* VIA VT1613 codec
*/
static const struct snd_kcontrol_new snd_ac97_controls_vt1613[] = {
AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0),
};
static int patch_vt1613_specific(struct snd_ac97 *ac97)
{
return patch_build_controls(ac97, &snd_ac97_controls_vt1613[0],
ARRAY_SIZE(snd_ac97_controls_vt1613));
};
static const struct snd_ac97_build_ops patch_vt1613_ops = {
.build_specific = patch_vt1613_specific
};
static int patch_vt1613(struct snd_ac97 *ac97)
{
ac97->build_ops = &patch_vt1613_ops;
ac97->flags |= AC97_HAS_NO_VIDEO;
ac97->caps |= AC97_BC_HEADPHONE;
return 0;
}
/*
* VIA VT1616 codec
*/

View file

@ -1385,8 +1385,8 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
.running)
&& (!chip->codecs[peer_codecs[codec_type].other2]
.running));
}
if (call_function)
}
if (call_function)
snd_azf3328_ctrl_enable_codecs(chip, enable);
/* ...and adjust clock, too
@ -2126,7 +2126,8 @@ static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
static int
snd_azf3328_pcm(struct snd_azf3328 *chip)
{
enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
/* pcm devices */
enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS };
struct snd_pcm *pcm;
int err;

View file

@ -2062,7 +2062,7 @@ static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol,
val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask;
if (reg.invert)
val = reg.mask - val;
ucontrol->value.integer.value[1] = val;
ucontrol->value.integer.value[1] = val;
}
spin_unlock_irq(&cm->reg_lock);
return 0;

View file

@ -29,8 +29,9 @@ MODULE_LICENSE("GPL");
/*
* create a new hardware dependent device for Emu10k1
*/
static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
static int snd_emu10k1_synth_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emux *emux;
struct snd_emu10k1 *hw;
struct snd_emu10k1_synth_arg *arg;
@ -79,8 +80,9 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
return 0;
}
static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
static int snd_emu10k1_synth_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emux *emux;
struct snd_emu10k1 *hw;
unsigned long flags;
@ -104,21 +106,14 @@ static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
* INIT part
*/
static int __init alsa_emu10k1_synth_init(void)
{
static struct snd_seq_dev_ops ops = {
snd_emu10k1_synth_new_device,
snd_emu10k1_synth_delete_device,
};
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops,
sizeof(struct snd_emu10k1_synth_arg));
}
static struct snd_seq_driver emu10k1_synth_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_emu10k1_synth_probe,
.remove = snd_emu10k1_synth_remove,
},
.id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
.argsize = sizeof(struct snd_emu10k1_synth_arg),
};
static void __exit alsa_emu10k1_synth_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH);
}
module_init(alsa_emu10k1_synth_init)
module_exit(alsa_emu10k1_synth_exit)
module_snd_seq_driver(emu10k1_synth_driver);

View file

@ -4,7 +4,7 @@ snd-hda-tegra-objs := hda_tegra.o
# for haswell power well
snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o

View file

@ -33,30 +33,36 @@ enum {
DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */
};
static void snd_hda_generate_beep(struct work_struct *work)
/* generate or stop tone */
static void generate_tone(struct hda_beep *beep, int tone)
{
struct hda_beep *beep =
container_of(work, struct hda_beep, beep_work);
struct hda_codec *codec = beep->codec;
int tone;
if (!beep->enabled)
return;
tone = beep->tone;
if (tone && !beep->playing) {
snd_hda_power_up(codec);
if (beep->power_hook)
beep->power_hook(beep, true);
beep->playing = 1;
}
/* generate tone */
snd_hda_codec_write(codec, beep->nid, 0,
AC_VERB_SET_BEEP_CONTROL, tone);
if (!tone && beep->playing) {
beep->playing = 0;
if (beep->power_hook)
beep->power_hook(beep, false);
snd_hda_power_down(codec);
}
}
static void snd_hda_generate_beep(struct work_struct *work)
{
struct hda_beep *beep =
container_of(work, struct hda_beep, beep_work);
if (beep->enabled)
generate_tone(beep, beep->tone);
}
/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
*
* The tone frequency of beep generator on IDT/STAC codecs is
@ -130,10 +136,7 @@ static void turn_off_beep(struct hda_beep *beep)
cancel_work_sync(&beep->beep_work);
if (beep->playing) {
/* turn off beep */
snd_hda_codec_write(beep->codec, beep->nid, 0,
AC_VERB_SET_BEEP_CONTROL, 0);
beep->playing = 0;
snd_hda_power_down(beep->codec);
generate_tone(beep, 0);
}
}
@ -160,6 +163,7 @@ static int snd_hda_do_attach(struct hda_beep *beep)
input_dev->name = "HDA Digital PCBeep";
input_dev->phys = beep->phys;
input_dev->id.bustype = BUS_PCI;
input_dev->dev.parent = &codec->card->card_dev;
input_dev->id.vendor = codec->vendor_id >> 16;
input_dev->id.product = codec->vendor_id & 0xffff;
@ -168,7 +172,6 @@ static int snd_hda_do_attach(struct hda_beep *beep)
input_dev->evbit[0] = BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
input_dev->event = snd_hda_beep_event;
input_dev->dev.parent = &codec->dev;
input_set_drvdata(input_dev, beep);
beep->dev = input_dev;
@ -224,7 +227,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
if (beep == NULL)
return -ENOMEM;
snprintf(beep->phys, sizeof(beep->phys),
"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
"card%d/codec#%d/beep0", codec->card->number, codec->addr);
/* enable linear scale */
snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0x01);

View file

@ -40,6 +40,7 @@ struct hda_beep {
unsigned int playing:1;
struct work_struct beep_work; /* scheduled task for beep event */
struct mutex mutex;
void (*power_hook)(struct hda_beep *beep, bool on);
};
#ifdef CONFIG_SND_HDA_INPUT_BEEP

342
sound/pci/hda/hda_bind.c Normal file
View file

@ -0,0 +1,342 @@
/*
* HD-audio codec driver binding
* Copyright (c) Takashi Iwai <tiwai@suse.de>
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/export.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
/* codec vendor labels */
struct hda_vendor_id {
unsigned int id;
const char *name;
};
static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x1002, "ATI" },
{ 0x1013, "Cirrus Logic" },
{ 0x1057, "Motorola" },
{ 0x1095, "Silicon Image" },
{ 0x10de, "Nvidia" },
{ 0x10ec, "Realtek" },
{ 0x1102, "Creative" },
{ 0x1106, "VIA" },
{ 0x111d, "IDT" },
{ 0x11c1, "LSI" },
{ 0x11d4, "Analog Devices" },
{ 0x13f6, "C-Media" },
{ 0x14f1, "Conexant" },
{ 0x17e8, "Chrontel" },
{ 0x1854, "LG" },
{ 0x1aec, "Wolfson Microelectronics" },
{ 0x1af4, "QEMU" },
{ 0x434d, "C-Media" },
{ 0x8086, "Intel" },
{ 0x8384, "SigmaTel" },
{} /* terminator */
};
/*
* find a matching codec preset
*/
static int hda_bus_match(struct device *dev, struct device_driver *drv)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, dev);
struct hda_codec_driver *driver =
container_of(drv, struct hda_codec_driver, driver);
const struct hda_codec_preset *preset;
/* check probe_id instead of vendor_id if set */
u32 id = codec->probe_id ? codec->probe_id : codec->vendor_id;
for (preset = driver->preset; preset->id; preset++) {
u32 mask = preset->mask;
if (preset->afg && preset->afg != codec->afg)
continue;
if (preset->mfg && preset->mfg != codec->mfg)
continue;
if (!mask)
mask = ~0;
if (preset->id == (id & mask) &&
(!preset->rev || preset->rev == codec->revision_id)) {
codec->preset = preset;
return 1;
}
}
return 0;
}
/* reset the codec name from the preset */
static int codec_refresh_name(struct hda_codec *codec, const char *name)
{
char tmp[16];
kfree(codec->chip_name);
if (!name) {
sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
name = tmp;
}
codec->chip_name = kstrdup(name, GFP_KERNEL);
return codec->chip_name ? 0 : -ENOMEM;
}
static int hda_codec_driver_probe(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
struct module *owner = dev->driver->owner;
int err;
if (WARN_ON(!codec->preset))
return -EINVAL;
err = codec_refresh_name(codec, codec->preset->name);
if (err < 0)
goto error;
if (!try_module_get(owner)) {
err = -EINVAL;
goto error;
}
err = codec->preset->patch(codec);
if (err < 0)
goto error_module;
err = snd_hda_codec_build_pcms(codec);
if (err < 0)
goto error_module;
err = snd_hda_codec_build_controls(codec);
if (err < 0)
goto error_module;
if (codec->card->registered) {
err = snd_card_register(codec->card);
if (err < 0)
goto error_module;
}
return 0;
error_module:
module_put(owner);
error:
snd_hda_codec_cleanup_for_unbind(codec);
return err;
}
static int hda_codec_driver_remove(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
snd_hda_codec_cleanup_for_unbind(codec);
module_put(dev->driver->owner);
return 0;
}
static void hda_codec_driver_shutdown(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
if (!pm_runtime_suspended(dev) && codec->patch_ops.reboot_notify)
codec->patch_ops.reboot_notify(codec);
}
int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
struct module *owner)
{
drv->driver.name = name;
drv->driver.owner = owner;
drv->driver.bus = &snd_hda_bus_type;
drv->driver.probe = hda_codec_driver_probe;
drv->driver.remove = hda_codec_driver_remove;
drv->driver.shutdown = hda_codec_driver_shutdown;
drv->driver.pm = &hda_codec_driver_pm;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__hda_codec_driver_register);
void hda_codec_driver_unregister(struct hda_codec_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(hda_codec_driver_unregister);
static inline bool codec_probed(struct hda_codec *codec)
{
return device_attach(hda_codec_dev(codec)) > 0 && codec->preset;
}
/* try to auto-load and bind the codec module */
static void codec_bind_module(struct hda_codec *codec)
{
#ifdef MODULE
request_module("snd-hda-codec-id:%08x", codec->vendor_id);
if (codec_probed(codec))
return;
request_module("snd-hda-codec-id:%04x*",
(codec->vendor_id >> 16) & 0xffff);
if (codec_probed(codec))
return;
#endif
}
/* store the codec vendor name */
static int get_codec_vendor_name(struct hda_codec *codec)
{
const struct hda_vendor_id *c;
const char *vendor = NULL;
u16 vendor_id = codec->vendor_id >> 16;
char tmp[16];
for (c = hda_vendor_ids; c->id; c++) {
if (c->id == vendor_id) {
vendor = c->name;
break;
}
}
if (!vendor) {
sprintf(tmp, "Generic %04x", vendor_id);
vendor = tmp;
}
codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
if (!codec->vendor_name)
return -ENOMEM;
return 0;
}
#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */
static bool is_likely_hdmi_codec(struct hda_codec *codec)
{
hda_nid_t nid = codec->start_nid;
int i;
for (i = 0; i < codec->num_nodes; i++, nid++) {
unsigned int wcaps = get_wcaps(codec, nid);
switch (get_wcaps_type(wcaps)) {
case AC_WID_AUD_IN:
return false; /* HDMI parser supports only HDMI out */
case AC_WID_AUD_OUT:
if (!(wcaps & AC_WCAP_DIGITAL))
return false;
break;
}
}
return true;
}
#else
/* no HDMI codec parser support */
#define is_likely_hdmi_codec(codec) false
#endif /* CONFIG_SND_HDA_CODEC_HDMI */
static int codec_bind_generic(struct hda_codec *codec)
{
if (codec->probe_id)
return -ENODEV;
if (is_likely_hdmi_codec(codec)) {
codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI;
#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
request_module("snd-hda-codec-hdmi");
#endif
if (codec_probed(codec))
return 0;
}
codec->probe_id = HDA_CODEC_ID_GENERIC;
#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
request_module("snd-hda-codec-generic");
#endif
if (codec_probed(codec))
return 0;
return -ENODEV;
}
#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
#define is_generic_config(codec) \
(codec->modelname && !strcmp(codec->modelname, "generic"))
#else
#define is_generic_config(codec) 0
#endif
/**
* snd_hda_codec_configure - (Re-)configure the HD-audio codec
* @codec: the HDA codec
*
* Start parsing of the given codec tree and (re-)initialize the whole
* patch instance.
*
* Returns 0 if successful or a negative error code.
*/
int snd_hda_codec_configure(struct hda_codec *codec)
{
int err;
if (!codec->vendor_name) {
err = get_codec_vendor_name(codec);
if (err < 0)
return err;
}
if (is_generic_config(codec))
codec->probe_id = HDA_CODEC_ID_GENERIC;
else
codec->probe_id = 0;
err = device_add(hda_codec_dev(codec));
if (err < 0)
return err;
if (!codec->preset)
codec_bind_module(codec);
if (!codec->preset) {
err = codec_bind_generic(codec);
if (err < 0) {
codec_err(codec, "Unable to bind the codec\n");
goto error;
}
}
/* audio codec should override the mixer name */
if (codec->afg || !*codec->card->mixername)
snprintf(codec->card->mixername,
sizeof(codec->card->mixername),
"%s %s", codec->vendor_name, codec->chip_name);
return 0;
error:
device_del(hda_codec_dev(codec));
return err;
}
EXPORT_SYMBOL_GPL(snd_hda_codec_configure);
/*
* bus registration
*/
struct bus_type snd_hda_bus_type = {
.name = "hdaudio",
.match = hda_bus_match,
};
static int __init hda_codec_init(void)
{
return bus_register(&snd_hda_bus_type);
}
static void __exit hda_codec_exit(void)
{
bus_unregister(&snd_hda_bus_type);
}
module_init(hda_codec_init);
module_exit(hda_codec_exit);

File diff suppressed because it is too large Load diff

View file

@ -21,6 +21,7 @@
#ifndef __SOUND_HDA_CODEC_H
#define __SOUND_HDA_CODEC_H
#include <linux/kref.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/pcm.h>
@ -66,7 +67,6 @@ struct hda_beep;
struct hda_codec;
struct hda_pcm;
struct hda_pcm_stream;
struct hda_bus_unsolicited;
/* NID type */
typedef u16 hda_nid_t;
@ -84,10 +84,6 @@ struct hda_bus_ops {
struct hda_pcm *pcm);
/* reset bus for retry verb */
void (*bus_reset)(struct hda_bus *bus);
#ifdef CONFIG_PM
/* notify power-up/down from codec to controller */
void (*pm_notify)(struct hda_bus *bus, bool power_up);
#endif
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* prepare DSP transfer */
int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format,
@ -101,13 +97,14 @@ struct hda_bus_ops {
#endif
};
/* template to pass to the bus constructor */
struct hda_bus_template {
void *private_data;
struct pci_dev *pci;
const char *modelname;
int *power_save;
struct hda_bus_ops ops;
/* unsolicited event handler */
#define HDA_UNSOL_QUEUE_SIZE 64
struct hda_bus_unsolicited {
/* ring buffer */
u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
unsigned int rp, wp;
/* workqueue */
struct work_struct work;
};
/*
@ -119,11 +116,9 @@ struct hda_bus_template {
struct hda_bus {
struct snd_card *card;
/* copied from template */
void *private_data;
struct pci_dev *pci;
const char *modelname;
int *power_save;
struct hda_bus_ops ops;
/* codec linked list */
@ -136,9 +131,7 @@ struct hda_bus {
struct mutex prepare_mutex;
/* unsolicited event queue */
struct hda_bus_unsolicited *unsol;
char workq_name[16];
struct workqueue_struct *workq; /* common workqueue for codecs */
struct hda_bus_unsolicited unsol;
/* assigned PCMs */
DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
@ -152,10 +145,10 @@ struct hda_bus {
unsigned int rirb_error:1; /* error in codec communication */
unsigned int response_reset:1; /* controller was reset */
unsigned int in_reset:1; /* during reset operation */
unsigned int power_keep_link_on:1; /* don't power off HDA link */
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
int primary_dig_out_type; /* primary digital out PCM type */
unsigned long codec_powered; /* bit flags of powered codecs */
};
/*
@ -175,15 +168,22 @@ struct hda_codec_preset {
int (*patch)(struct hda_codec *codec);
};
struct hda_codec_preset_list {
#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101
#define HDA_CODEC_ID_GENERIC 0x00000201
struct hda_codec_driver {
struct device_driver driver;
const struct hda_codec_preset *preset;
struct module *owner;
struct list_head list;
};
/* initial hook */
int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset);
int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset);
int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
struct module *owner);
#define hda_codec_driver_register(drv) \
__hda_codec_driver_register(drv, KBUILD_MODNAME, THIS_MODULE)
void hda_codec_driver_unregister(struct hda_codec_driver *drv);
#define module_hda_codec_driver(drv) \
module_driver(drv, hda_codec_driver_register, \
hda_codec_driver_unregister)
/* ops set by the preset patch */
struct hda_codec_ops {
@ -200,6 +200,7 @@ struct hda_codec_ops {
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
#endif
void (*reboot_notify)(struct hda_codec *codec);
void (*stream_pm)(struct hda_codec *codec, hda_nid_t nid, bool on);
};
/* record for amp information cache */
@ -267,12 +268,17 @@ struct hda_pcm {
int device; /* device number to assign */
struct snd_pcm *pcm; /* assigned PCM instance */
bool own_chmap; /* codec driver provides own channel maps */
/* private: */
struct hda_codec *codec;
struct kref kref;
struct list_head list;
};
/* codec information */
struct hda_codec {
struct device dev;
struct hda_bus *bus;
struct snd_card *card;
unsigned int addr; /* codec addr*/
struct list_head list; /* list point */
@ -287,11 +293,10 @@ struct hda_codec {
u32 vendor_id;
u32 subsystem_id;
u32 revision_id;
u32 probe_id; /* overridden id for probing */
/* detected preset */
const struct hda_codec_preset *preset;
struct module *owner;
int (*parser)(struct hda_codec *codec);
const char *vendor_name; /* codec vendor name */
const char *chip_name; /* codec chip name */
const char *modelname; /* model name for preset */
@ -300,8 +305,7 @@ struct hda_codec {
struct hda_codec_ops patch_ops;
/* PCM to create, set by patch_ops.build_pcms callback */
unsigned int num_pcms;
struct hda_pcm *pcm_info;
struct list_head pcm_list_head;
/* codec specific info */
void *spec;
@ -345,6 +349,7 @@ struct hda_codec {
#endif
/* misc flags */
unsigned int in_freeing:1; /* being released */
unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
* status change
* (e.g. Realtek codecs)
@ -366,18 +371,13 @@ struct hda_codec {
unsigned int cached_write:1; /* write only to caches */
unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
unsigned int power_save_node:1; /* advanced PM for each widget */
#ifdef CONFIG_PM
unsigned int power_on :1; /* current (global) power-state */
unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
unsigned int pm_up_notified:1; /* PM notified to controller */
unsigned int in_pm:1; /* suspend/resume being performed */
int power_transition; /* power-state in transition */
int power_count; /* current (global) power refcount */
struct delayed_work power_work; /* delayed task for powerdown */
atomic_t in_pm; /* suspend/resume being performed */
unsigned long power_on_acct;
unsigned long power_off_acct;
unsigned long power_jiffies;
spinlock_t power_lock;
#endif
/* filter the requested power state per nid */
@ -409,6 +409,11 @@ struct hda_codec {
struct snd_array verbs;
};
#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, dev)
#define hda_codec_dev(_dev) (&(_dev)->dev)
extern struct bus_type snd_hda_bus_type;
/* direction */
enum {
HDA_INPUT, HDA_OUTPUT
@ -420,10 +425,9 @@ enum {
/*
* constructors
*/
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,
struct hda_codec **codecp);
int snd_hda_bus_new(struct snd_card *card, struct hda_bus **busp);
int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
unsigned int codec_addr, struct hda_codec **codecp);
int snd_hda_codec_configure(struct hda_codec *codec);
int snd_hda_codec_update_widgets(struct hda_codec *codec);
@ -512,15 +516,24 @@ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid);
/*
* Mixer
*/
int snd_hda_build_controls(struct hda_bus *bus);
int snd_hda_codec_build_controls(struct hda_codec *codec);
/*
* PCM
*/
int snd_hda_build_pcms(struct hda_bus *bus);
int snd_hda_codec_parse_pcms(struct hda_codec *codec);
int snd_hda_codec_build_pcms(struct hda_codec *codec);
__printf(2, 3)
struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
const char *fmt, ...);
static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm)
{
kref_get(&pcm->kref);
}
void snd_hda_codec_pcm_put(struct hda_pcm *pcm);
int snd_hda_codec_prepare(struct hda_codec *codec,
struct hda_pcm_stream *hinfo,
unsigned int stream,
@ -552,20 +565,17 @@ extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
* Misc
*/
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
void snd_hda_bus_reboot_notify(struct hda_bus *bus);
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
int snd_hda_lock_devices(struct hda_bus *bus);
void snd_hda_unlock_devices(struct hda_bus *bus);
void snd_hda_bus_reset(struct hda_bus *bus);
/*
* power management
*/
#ifdef CONFIG_PM
int snd_hda_suspend(struct hda_bus *bus);
int snd_hda_resume(struct hda_bus *bus);
#endif
extern const struct dev_pm_ops hda_codec_driver_pm;
static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
@ -588,64 +598,16 @@ const char *snd_hda_get_jack_location(u32 cfg);
* power saving
*/
#ifdef CONFIG_PM
void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait);
void snd_hda_power_up(struct hda_codec *codec);
void snd_hda_power_down(struct hda_codec *codec);
void snd_hda_set_power_save(struct hda_bus *bus, int delay);
void snd_hda_update_power_acct(struct hda_codec *codec);
#else
static inline void snd_hda_power_save(struct hda_codec *codec, int delta,
bool d3wait) {}
static inline void snd_hda_power_up(struct hda_codec *codec) {}
static inline void snd_hda_power_down(struct hda_codec *codec) {}
static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {}
#endif
/**
* snd_hda_power_up - Power-up the codec
* @codec: HD-audio codec
*
* Increment the power-up counter and power up the hardware really when
* not turned on yet.
*/
static inline void snd_hda_power_up(struct hda_codec *codec)
{
snd_hda_power_save(codec, 1, false);
}
/**
* snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending
* D3 transition to complete. This differs from snd_hda_power_up() when
* power_transition == -1. snd_hda_power_up sees this case as a nop,
* snd_hda_power_up_d3wait waits for the D3 transition to complete then powers
* back up.
* @codec: HD-audio codec
*
* Cancel any power down operation hapenning on the work queue, then power up.
*/
static inline void snd_hda_power_up_d3wait(struct hda_codec *codec)
{
snd_hda_power_save(codec, 1, true);
}
/**
* snd_hda_power_down - Power-down the codec
* @codec: HD-audio codec
*
* Decrement the power-up counter and schedules the power-off work if
* the counter rearches to zero.
*/
static inline void snd_hda_power_down(struct hda_codec *codec)
{
snd_hda_power_save(codec, -1, false);
}
/**
* snd_hda_power_sync - Synchronize the power-save status
* @codec: HD-audio codec
*
* Synchronize the actual power state with the power account;
* called when power_save parameter is changed
*/
static inline void snd_hda_power_sync(struct hda_codec *codec)
{
snd_hda_power_save(codec, 0, false);
}
#ifdef CONFIG_SND_HDA_PATCH_LOADER
/*
* patch firmware

View file

@ -27,10 +27,8 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/reboot.h>
#include <sound/core.h>
#include <sound/initval.h>
#include "hda_priv.h"
#include "hda_controller.h"
#define CREATE_TRACE_POINTS
@ -259,11 +257,18 @@ static void azx_timecounter_init(struct snd_pcm_substream *substream,
tc->cycle_last = last;
}
static inline struct hda_pcm_stream *
to_hda_pcm_stream(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
return &apcm->info->stream[substream->stream];
}
static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream,
u64 nsec)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
u64 codec_frames, codec_nsecs;
if (!hinfo->ops.get_delay)
@ -399,7 +404,7 @@ static int azx_setup_periods(struct azx *chip,
static int azx_pcm_close(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev = get_azx_dev(substream);
unsigned long flags;
@ -410,9 +415,11 @@ static int azx_pcm_close(struct snd_pcm_substream *substream)
azx_dev->running = 0;
spin_unlock_irqrestore(&chip->reg_lock, flags);
azx_release_device(azx_dev);
hinfo->ops.close(hinfo, apcm->codec, substream);
if (hinfo->ops.close)
hinfo->ops.close(hinfo, apcm->codec, substream);
snd_hda_power_down(apcm->codec);
mutex_unlock(&chip->open_mutex);
snd_hda_codec_pcm_put(apcm->info);
return 0;
}
@ -441,7 +448,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx_dev *azx_dev = get_azx_dev(substream);
struct azx *chip = apcm->chip;
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
int err;
/* reset BDL address */
@ -468,7 +475,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int bufsize, period_bytes, format_val, stream_tag;
int err;
@ -708,7 +715,7 @@ unsigned int azx_get_position(struct azx *chip,
if (substream->runtime) {
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[stream];
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
if (chip->get_delay[stream])
delay += chip->get_delay[stream](chip, azx_dev, pos);
@ -732,17 +739,32 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
azx_get_position(chip, azx_dev));
}
static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream,
struct timespec *ts)
static int azx_get_time_info(struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
{
struct azx_dev *azx_dev = get_azx_dev(substream);
u64 nsec;
nsec = timecounter_read(&azx_dev->azx_tc);
nsec = div_u64(nsec, 3); /* can be optimized */
nsec = azx_adjust_codec_delay(substream, nsec);
if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
(audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
*ts = ns_to_timespec(nsec);
snd_pcm_gettime(substream->runtime, system_ts);
nsec = timecounter_read(&azx_dev->azx_tc);
nsec = div_u64(nsec, 3); /* can be optimized */
if (audio_tstamp_config->report_delay)
nsec = azx_adjust_codec_delay(substream, nsec);
*audio_ts = ns_to_timespec(nsec);
audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */
} else
audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
return 0;
}
@ -756,7 +778,8 @@ static struct snd_pcm_hardware azx_pcm_hw = {
/* SNDRV_PCM_INFO_RESUME |*/
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_HAS_WALL_CLOCK |
SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
SNDRV_PCM_INFO_HAS_LINK_ATIME |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
@ -775,7 +798,7 @@ static struct snd_pcm_hardware azx_pcm_hw = {
static int azx_pcm_open(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev;
struct snd_pcm_runtime *runtime = substream->runtime;
@ -783,11 +806,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
int err;
int buff_step;
snd_hda_codec_pcm_get(apcm->info);
mutex_lock(&chip->open_mutex);
azx_dev = azx_assign_device(chip, substream);
if (azx_dev == NULL) {
mutex_unlock(&chip->open_mutex);
return -EBUSY;
err = -EBUSY;
goto unlock;
}
runtime->hw = azx_pcm_hw;
runtime->hw.channels_min = hinfo->channels_min;
@ -821,13 +845,14 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
buff_step);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
buff_step);
snd_hda_power_up_d3wait(apcm->codec);
err = hinfo->ops.open(hinfo, apcm->codec, substream);
snd_hda_power_up(apcm->codec);
if (hinfo->ops.open)
err = hinfo->ops.open(hinfo, apcm->codec, substream);
else
err = -ENODEV;
if (err < 0) {
azx_release_device(azx_dev);
snd_hda_power_down(apcm->codec);
mutex_unlock(&chip->open_mutex);
return err;
goto powerdown;
}
snd_pcm_limit_hw_rates(runtime);
/* sanity check */
@ -836,16 +861,18 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
snd_BUG_ON(!runtime->hw.formats) ||
snd_BUG_ON(!runtime->hw.rates)) {
azx_release_device(azx_dev);
hinfo->ops.close(hinfo, apcm->codec, substream);
snd_hda_power_down(apcm->codec);
mutex_unlock(&chip->open_mutex);
return -EINVAL;
if (hinfo->ops.close)
hinfo->ops.close(hinfo, apcm->codec, substream);
err = -EINVAL;
goto powerdown;
}
/* disable WALLCLOCK timestamps for capture streams
/* disable LINK_ATIME timestamps for capture streams
until we figure out how to handle digital inputs */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */
runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
}
spin_lock_irqsave(&chip->reg_lock, flags);
azx_dev->substream = substream;
@ -856,6 +883,13 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
mutex_unlock(&chip->open_mutex);
return 0;
powerdown:
snd_hda_power_down(apcm->codec);
unlock:
mutex_unlock(&chip->open_mutex);
snd_hda_codec_pcm_put(apcm->info);
return err;
}
static int azx_pcm_mmap(struct snd_pcm_substream *substream,
@ -877,7 +911,7 @@ static struct snd_pcm_ops azx_pcm_ops = {
.prepare = azx_pcm_prepare,
.trigger = azx_pcm_trigger,
.pointer = azx_pcm_pointer,
.wall_clock = azx_get_wallclock_tstamp,
.get_time_info = azx_get_time_info,
.mmap = azx_pcm_mmap,
.page = snd_pcm_sgbuf_ops_page,
};
@ -887,6 +921,7 @@ static void azx_pcm_free(struct snd_pcm *pcm)
struct azx_pcm *apcm = pcm->private_data;
if (apcm) {
list_del(&apcm->list);
apcm->info->pcm = NULL;
kfree(apcm);
}
}
@ -923,6 +958,7 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
apcm->chip = chip;
apcm->pcm = pcm;
apcm->codec = codec;
apcm->info = cpcm;
pcm->private_data = apcm;
pcm->private_free = azx_pcm_free;
if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
@ -930,7 +966,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
list_add_tail(&apcm->list, &chip->pcm_list);
cpcm->pcm = pcm;
for (s = 0; s < 2; s++) {
apcm->hinfo[s] = &cpcm->stream[s];
if (cpcm->stream[s].substreams)
snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
}
@ -941,9 +976,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
chip->card->dev,
size, MAX_PREALLOC_SIZE);
/* link to codec */
for (s = 0; s < 2; s++)
pcm->streams[s].dev.parent = &codec->dev;
return 0;
}
@ -952,14 +984,9 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
*/
static int azx_alloc_cmd_io(struct azx *chip)
{
int err;
/* single page (at least 4096 bytes) must suffice for both ringbuffes */
err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
PAGE_SIZE, &chip->rb);
if (err < 0)
dev_err(chip->card->dev, "cannot allocate CORB/RIRB\n");
return err;
return chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
PAGE_SIZE, &chip->rb);
}
static void azx_init_cmd_io(struct azx *chip)
@ -1445,7 +1472,6 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus,
int azx_alloc_stream_pages(struct azx *chip)
{
int i, err;
struct snd_card *card = chip->card;
for (i = 0; i < chip->num_streams; i++) {
dsp_lock_init(&chip->azx_dev[i]);
@ -1453,18 +1479,14 @@ int azx_alloc_stream_pages(struct azx *chip)
err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
BDL_SIZE,
&chip->azx_dev[i].bdl);
if (err < 0) {
dev_err(card->dev, "cannot allocate BDL\n");
if (err < 0)
return -ENOMEM;
}
}
/* allocate memory for the position buffer */
err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
chip->num_streams * 8, &chip->posbuf);
if (err < 0) {
dev_err(card->dev, "cannot allocate posbuf\n");
if (err < 0)
return -ENOMEM;
}
/* allocate CORB/RIRB */
err = azx_alloc_cmd_io(chip);
@ -1676,7 +1698,7 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
int i;
#ifdef CONFIG_PM
if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
if (azx_has_pm_runtime(chip))
if (!pm_runtime_active(chip->card->dev))
return IRQ_NONE;
#endif
@ -1761,34 +1783,11 @@ static void azx_bus_reset(struct hda_bus *bus)
bus->in_reset = 1;
azx_stop_chip(chip);
azx_init_chip(chip, true);
#ifdef CONFIG_PM
if (chip->initialized) {
struct azx_pcm *p;
list_for_each_entry(p, &chip->pcm_list, list)
snd_pcm_suspend_all(p->pcm);
snd_hda_suspend(chip->bus);
snd_hda_resume(chip->bus);
}
#endif
if (chip->initialized)
snd_hda_bus_reset(chip->bus);
bus->in_reset = 0;
}
#ifdef CONFIG_PM
/* power-up/down the controller */
static void azx_power_notify(struct hda_bus *bus, bool power_up)
{
struct azx *chip = bus->private_data;
if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
return;
if (power_up)
pm_runtime_get_sync(chip->card->dev);
else
pm_runtime_put_sync(chip->card->dev);
}
#endif
static int get_jackpoll_interval(struct azx *chip)
{
int i;
@ -1810,41 +1809,59 @@ static int get_jackpoll_interval(struct azx *chip)
return j;
}
/* Codec initialization */
int azx_codec_create(struct azx *chip, const char *model,
unsigned int max_slots,
int *power_save_to)
{
struct hda_bus_template bus_temp;
int c, codecs, err;
memset(&bus_temp, 0, sizeof(bus_temp));
bus_temp.private_data = chip;
bus_temp.modelname = model;
bus_temp.pci = chip->pci;
bus_temp.ops.command = azx_send_cmd;
bus_temp.ops.get_response = azx_get_response;
bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
bus_temp.ops.bus_reset = azx_bus_reset;
#ifdef CONFIG_PM
bus_temp.power_save = power_save_to;
bus_temp.ops.pm_notify = azx_power_notify;
#endif
static struct hda_bus_ops bus_ops = {
.command = azx_send_cmd,
.get_response = azx_get_response,
.attach_pcm = azx_attach_pcm_stream,
.bus_reset = azx_bus_reset,
#ifdef CONFIG_SND_HDA_DSP_LOADER
bus_temp.ops.load_dsp_prepare = azx_load_dsp_prepare;
bus_temp.ops.load_dsp_trigger = azx_load_dsp_trigger;
bus_temp.ops.load_dsp_cleanup = azx_load_dsp_cleanup;
.load_dsp_prepare = azx_load_dsp_prepare,
.load_dsp_trigger = azx_load_dsp_trigger,
.load_dsp_cleanup = azx_load_dsp_cleanup,
#endif
};
err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
/* HD-audio bus initialization */
int azx_bus_create(struct azx *chip, const char *model)
{
struct hda_bus *bus;
int err;
err = snd_hda_bus_new(chip->card, &bus);
if (err < 0)
return err;
chip->bus = bus;
bus->private_data = chip;
bus->pci = chip->pci;
bus->modelname = model;
bus->ops = bus_ops;
if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) {
dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
chip->bus->needs_damn_long_delay = 1;
bus->needs_damn_long_delay = 1;
}
/* AMD chipsets often cause the communication stalls upon certain
* sequence like the pin-detection. It seems that forcing the synced
* access works around the stall. Grrr...
*/
if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) {
dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n");
bus->sync_write = 1;
bus->allow_bus_reset = 1;
}
return 0;
}
EXPORT_SYMBOL_GPL(azx_bus_create);
/* Probe codecs */
int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
{
struct hda_bus *bus = chip->bus;
int c, codecs, err;
codecs = 0;
if (!max_slots)
max_slots = AZX_DEFAULT_CODECS;
@ -1872,21 +1889,11 @@ int azx_codec_create(struct azx *chip, const char *model,
}
}
/* AMD chipsets often cause the communication stalls upon certain
* sequence like the pin-detection. It seems that forcing the synced
* access works around the stall. Grrr...
*/
if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) {
dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n");
chip->bus->sync_write = 1;
chip->bus->allow_bus_reset = 1;
}
/* Then create codec instances */
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, &codec);
err = snd_hda_codec_new(bus, bus->card, c, &codec);
if (err < 0)
continue;
codec->jackpoll_interval = get_jackpoll_interval(chip);
@ -1900,7 +1907,7 @@ int azx_codec_create(struct azx *chip, const char *model,
}
return 0;
}
EXPORT_SYMBOL_GPL(azx_codec_create);
EXPORT_SYMBOL_GPL(azx_probe_codecs);
/* configure each codec instance */
int azx_codec_configure(struct azx *chip)
@ -1913,13 +1920,6 @@ int azx_codec_configure(struct azx *chip)
}
EXPORT_SYMBOL_GPL(azx_codec_configure);
/* mixer creation - all stuff is implemented in hda module */
int azx_mixer_create(struct azx *chip)
{
return snd_hda_build_controls(chip->bus);
}
EXPORT_SYMBOL_GPL(azx_mixer_create);
static bool is_input_stream(struct azx *chip, unsigned char index)
{
@ -1966,30 +1966,5 @@ int azx_init_stream(struct azx *chip)
}
EXPORT_SYMBOL_GPL(azx_init_stream);
/*
* reboot notifier for hang-up problem at power-down
*/
static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
{
struct azx *chip = container_of(nb, struct azx, reboot_notifier);
snd_hda_bus_reboot_notify(chip->bus);
azx_stop_chip(chip);
return NOTIFY_OK;
}
void azx_notifier_register(struct azx *chip)
{
chip->reboot_notifier.notifier_call = azx_halt;
register_reboot_notifier(&chip->reboot_notifier);
}
EXPORT_SYMBOL_GPL(azx_notifier_register);
void azx_notifier_unregister(struct azx *chip)
{
if (chip->reboot_notifier.notifier_call)
unregister_reboot_notifier(&chip->reboot_notifier);
}
EXPORT_SYMBOL_GPL(azx_notifier_unregister);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Common HDA driver functions");

View file

@ -15,10 +15,396 @@
#ifndef __SOUND_HDA_CONTROLLER_H
#define __SOUND_HDA_CONTROLLER_H
#include <linux/timecounter.h>
#include <linux/interrupt.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include "hda_codec.h"
#include "hda_priv.h"
/*
* registers
*/
#define AZX_REG_GCAP 0x00
#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */
#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */
#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */
#define AZX_GCAP_ISS (15 << 8) /* # of input streams */
#define AZX_GCAP_OSS (15 << 12) /* # of output streams */
#define AZX_REG_VMIN 0x02
#define AZX_REG_VMAJ 0x03
#define AZX_REG_OUTPAY 0x04
#define AZX_REG_INPAY 0x06
#define AZX_REG_GCTL 0x08
#define AZX_GCTL_RESET (1 << 0) /* controller reset */
#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */
#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
#define AZX_REG_WAKEEN 0x0c
#define AZX_REG_STATESTS 0x0e
#define AZX_REG_GSTS 0x10
#define AZX_GSTS_FSTS (1 << 1) /* flush status */
#define AZX_REG_INTCTL 0x20
#define AZX_REG_INTSTS 0x24
#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */
#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */
#define AZX_REG_SSYNC 0x38
#define AZX_REG_CORBLBASE 0x40
#define AZX_REG_CORBUBASE 0x44
#define AZX_REG_CORBWP 0x48
#define AZX_REG_CORBRP 0x4a
#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */
#define AZX_REG_CORBCTL 0x4c
#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */
#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
#define AZX_REG_CORBSTS 0x4d
#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */
#define AZX_REG_CORBSIZE 0x4e
#define AZX_REG_RIRBLBASE 0x50
#define AZX_REG_RIRBUBASE 0x54
#define AZX_REG_RIRBWP 0x58
#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */
#define AZX_REG_RINTCNT 0x5a
#define AZX_REG_RIRBCTL 0x5c
#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */
#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
#define AZX_REG_RIRBSTS 0x5d
#define AZX_RBSTS_IRQ (1 << 0) /* response irq */
#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */
#define AZX_REG_RIRBSIZE 0x5e
#define AZX_REG_IC 0x60
#define AZX_REG_IR 0x64
#define AZX_REG_IRS 0x68
#define AZX_IRS_VALID (1<<1)
#define AZX_IRS_BUSY (1<<0)
#define AZX_REG_DPLBASE 0x70
#define AZX_REG_DPUBASE 0x74
#define AZX_DPLBASE_ENABLE 0x1 /* Enable position buffer */
/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* stream register offsets from stream base */
#define AZX_REG_SD_CTL 0x00
#define AZX_REG_SD_STS 0x03
#define AZX_REG_SD_LPIB 0x04
#define AZX_REG_SD_CBL 0x08
#define AZX_REG_SD_LVI 0x0c
#define AZX_REG_SD_FIFOW 0x0e
#define AZX_REG_SD_FIFOSIZE 0x10
#define AZX_REG_SD_FORMAT 0x12
#define AZX_REG_SD_BDLPL 0x18
#define AZX_REG_SD_BDLPU 0x1c
/* PCI space */
#define AZX_PCIREG_TCSEL 0x44
/*
* other constants
*/
/* max number of fragments - we may use more if allocating more pages for BDL */
#define BDL_SIZE 4096
#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
#define AZX_MAX_FRAG 32
/* max buffer size - no h/w limit, you can increase as you like */
#define AZX_MAX_BUF_SIZE (1024*1024*1024)
/* RIRB int mask: overrun[2], response[0] */
#define RIRB_INT_RESPONSE 0x01
#define RIRB_INT_OVERRUN 0x04
#define RIRB_INT_MASK 0x05
/* STATESTS int mask: S3,SD2,SD1,SD0 */
#define AZX_MAX_CODECS 8
#define AZX_DEFAULT_CODECS 4
#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
#define SD_CTL_STRIPE (3 << 16) /* stripe control */
#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
#define SD_CTL_STREAM_TAG_SHIFT 20
/* SD_CTL and SD_STS */
#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
#define SD_INT_COMPLETE 0x04 /* completion interrupt */
#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
SD_INT_COMPLETE)
/* SD_STS */
#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
/* INTCTL and INTSTS */
#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */
#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
/* below are so far hardcoded - should read registers in future */
#define AZX_MAX_CORB_ENTRIES 256
#define AZX_MAX_RIRB_ENTRIES 256
/* driver quirks (capabilities) */
/* bits 0-7 are used for indicating driver type */
#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */
#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */
#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */
#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */
#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */
#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */
#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */
#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */
#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */
#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */
#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */
/* 22 unused */
#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
#define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */
#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */
#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */
enum {
AZX_SNOOP_TYPE_NONE,
AZX_SNOOP_TYPE_SCH,
AZX_SNOOP_TYPE_ATI,
AZX_SNOOP_TYPE_NVIDIA,
};
/* HD Audio class code */
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
struct azx_dev {
struct snd_dma_buffer bdl; /* BDL buffer */
u32 *posbuf; /* position buffer pointer */
unsigned int bufsize; /* size of the play buffer in bytes */
unsigned int period_bytes; /* size of the period in bytes */
unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */
unsigned long start_wallclk; /* start + minimum wallclk */
unsigned long period_wallclk; /* wallclk for period */
void __iomem *sd_addr; /* stream descriptor pointer */
u32 sd_int_sta_mask; /* stream int status mask */
/* pcm support */
struct snd_pcm_substream *substream; /* assigned substream,
* set in PCM open
*/
unsigned int format_val; /* format value to be set in the
* controller and the codec
*/
unsigned char stream_tag; /* assigned stream */
unsigned char index; /* stream index */
int assigned_key; /* last device# key assigned to */
unsigned int opened:1;
unsigned int running:1;
unsigned int irq_pending:1;
unsigned int prepared:1;
unsigned int locked:1;
/*
* For VIA:
* A flag to ensure DMA position is 0
* when link position is not greater than FIFO size
*/
unsigned int insufficient:1;
unsigned int wc_marked:1;
unsigned int no_period_wakeup:1;
struct timecounter azx_tc;
struct cyclecounter azx_cc;
int delay_negative_threshold;
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* Allows dsp load to have sole access to the playback stream. */
struct mutex dsp_mutex;
#endif
};
/* CORB/RIRB */
struct azx_rb {
u32 *buf; /* CORB/RIRB buffer
* Each CORB entry is 4byte, RIRB is 8byte
*/
dma_addr_t addr; /* physical address of CORB/RIRB buffer */
/* for RIRB */
unsigned short rp, wp; /* read/write pointers */
int cmds[AZX_MAX_CODECS]; /* number of pending requests */
u32 res[AZX_MAX_CODECS]; /* last read value */
};
struct azx;
/* Functions to read/write to hda registers. */
struct hda_controller_ops {
/* Register Access */
void (*reg_writel)(u32 value, u32 __iomem *addr);
u32 (*reg_readl)(u32 __iomem *addr);
void (*reg_writew)(u16 value, u16 __iomem *addr);
u16 (*reg_readw)(u16 __iomem *addr);
void (*reg_writeb)(u8 value, u8 __iomem *addr);
u8 (*reg_readb)(u8 __iomem *addr);
/* Disable msi if supported, PCI only */
int (*disable_msi_reset_irq)(struct azx *);
/* Allocation ops */
int (*dma_alloc_pages)(struct azx *chip,
int type,
size_t size,
struct snd_dma_buffer *buf);
void (*dma_free_pages)(struct azx *chip, struct snd_dma_buffer *buf);
int (*substream_alloc_pages)(struct azx *chip,
struct snd_pcm_substream *substream,
size_t size);
int (*substream_free_pages)(struct azx *chip,
struct snd_pcm_substream *substream);
void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream,
struct vm_area_struct *area);
/* Check if current position is acceptable */
int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
};
struct azx_pcm {
struct azx *chip;
struct snd_pcm *pcm;
struct hda_codec *codec;
struct hda_pcm *info;
struct list_head list;
};
typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *);
typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos);
struct azx {
struct snd_card *card;
struct pci_dev *pci;
int dev_index;
/* chip type specific */
int driver_type;
unsigned int driver_caps;
int playback_streams;
int playback_index_offset;
int capture_streams;
int capture_index_offset;
int num_streams;
const int *jackpoll_ms; /* per-card jack poll interval */
/* Register interaction. */
const struct hda_controller_ops *ops;
/* position adjustment callbacks */
azx_get_pos_callback_t get_position[2];
azx_get_delay_callback_t get_delay[2];
/* pci resources */
unsigned long addr;
void __iomem *remap_addr;
int irq;
/* locks */
spinlock_t reg_lock;
struct mutex open_mutex; /* Prevents concurrent open/close operations */
/* streams (x num_streams) */
struct azx_dev *azx_dev;
/* PCM */
struct list_head pcm_list; /* azx_pcm list */
/* HD codec */
unsigned short codec_mask;
int codec_probe_mask; /* copied from probe_mask option */
struct hda_bus *bus;
unsigned int beep_mode;
/* CORB/RIRB */
struct azx_rb corb;
struct azx_rb rirb;
/* CORB/RIRB and position buffers */
struct snd_dma_buffer rb;
struct snd_dma_buffer posbuf;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
const struct firmware *fw;
#endif
/* flags */
const int *bdl_pos_adj;
int poll_count;
unsigned int running:1;
unsigned int initialized:1;
unsigned int single_cmd:1;
unsigned int polling_mode:1;
unsigned int msi:1;
unsigned int probing:1; /* codec probing phase */
unsigned int snoop:1;
unsigned int align_buffer_size:1;
unsigned int region_requested:1;
unsigned int disabled:1; /* disabled by VGA-switcher */
/* for debugging */
unsigned int last_cmd[AZX_MAX_CODECS];
#ifdef CONFIG_SND_HDA_DSP_LOADER
struct azx_dev saved_azx_dev;
#endif
};
#ifdef CONFIG_X86
#define azx_snoop(chip) ((chip)->snoop)
#else
#define azx_snoop(chip) true
#endif
/*
* macros for easy use
*/
#define azx_writel(chip, reg, value) \
((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg))
#define azx_readl(chip, reg) \
((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg))
#define azx_writew(chip, reg, value) \
((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg))
#define azx_readw(chip, reg) \
((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg))
#define azx_writeb(chip, reg, value) \
((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg))
#define azx_readb(chip, reg) \
((chip)->ops->reg_readb((chip)->remap_addr + AZX_REG_##reg))
#define azx_sd_writel(chip, dev, reg, value) \
((chip)->ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readl(chip, dev, reg) \
((chip)->ops->reg_readl((dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_writew(chip, dev, reg, value) \
((chip)->ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readw(chip, dev, reg) \
((chip)->ops->reg_readw((dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_writeb(chip, dev, reg, value) \
((chip)->ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readb(chip, dev, reg) \
((chip)->ops->reg_readb((dev)->sd_addr + AZX_REG_##reg))
#define azx_has_pm_runtime(chip) \
(!AZX_DCAPS_PM_RUNTIME || ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME))
/* PCM setup */
static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream)
@ -43,14 +429,9 @@ void azx_enter_link_reset(struct azx *chip);
irqreturn_t azx_interrupt(int irq, void *dev_id);
/* Codec interface */
int azx_codec_create(struct azx *chip, const char *model,
unsigned int max_slots,
int *power_save_to);
int azx_bus_create(struct azx *chip, const char *model);
int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
int azx_codec_configure(struct azx *chip);
int azx_mixer_create(struct azx *chip);
int azx_init_stream(struct azx *chip);
void azx_notifier_register(struct azx *chip);
void azx_notifier_unregister(struct azx *chip);
#endif /* __SOUND_HDA_CONTROLLER_H */

View file

@ -140,6 +140,9 @@ static void parse_user_hints(struct hda_codec *codec)
val = snd_hda_get_bool_hint(codec, "single_adc_amp");
if (val >= 0)
codec->single_adc_amp = !!val;
val = snd_hda_get_bool_hint(codec, "power_save_node");
if (val >= 0)
codec->power_save_node = !!val;
val = snd_hda_get_bool_hint(codec, "auto_mute");
if (val >= 0)
@ -648,12 +651,24 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
unsigned int dir, unsigned int idx)
{
struct hda_gen_spec *spec = codec->spec;
int type = get_wcaps_type(get_wcaps(codec, nid));
int i, n;
if (nid == codec->afg)
return true;
for (n = 0; n < spec->paths.used; n++) {
struct nid_path *path = snd_array_elem(&spec->paths, n);
if (!path->active)
continue;
if (codec->power_save_node) {
if (!path->stream_enabled)
continue;
/* ignore unplugged paths except for DAC/ADC */
if (!(path->pin_enabled || path->pin_fixed) &&
type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN)
continue;
}
for (i = 0; i < path->depth; i++) {
if (path->path[i] == nid) {
if (dir == HDA_OUTPUT || path->idx[i] == idx)
@ -807,6 +822,44 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
}
}
/* sync power of each widget in the the given path */
static hda_nid_t path_power_update(struct hda_codec *codec,
struct nid_path *path,
bool allow_powerdown)
{
hda_nid_t nid, changed = 0;
int i, state;
for (i = 0; i < path->depth; i++) {
nid = path->path[i];
if (nid == codec->afg)
continue;
if (!allow_powerdown || is_active_nid_for_any(codec, nid))
state = AC_PWRST_D0;
else
state = AC_PWRST_D3;
if (!snd_hda_check_power_state(codec, nid, state)) {
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_POWER_STATE, state);
changed = nid;
/* here we assume that widget attributes (e.g. amp,
* pinctl connection) don't change with local power
* state change. If not, need to sync the cache.
*/
}
}
return changed;
}
/* do sync with the last power state change */
static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid)
{
if (nid) {
msleep(10);
snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
}
}
/**
* snd_hda_activate_path - activate or deactivate the given path
* @codec: the HDA codec
@ -825,15 +878,13 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
if (!enable)
path->active = false;
/* make sure the widget is powered up */
if (enable && (spec->power_down_unused || codec->power_save_node))
path_power_update(codec, path, codec->power_save_node);
for (i = path->depth - 1; i >= 0; i--) {
hda_nid_t nid = path->path[i];
if (enable && spec->power_down_unused) {
/* make sure the widget is powered up */
if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0))
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_POWER_STATE,
AC_PWRST_D0);
}
if (enable && path->multi[i])
snd_hda_codec_update_cache(codec, nid, 0,
AC_VERB_SET_CONNECT_SEL,
@ -853,28 +904,10 @@ EXPORT_SYMBOL_GPL(snd_hda_activate_path);
static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path)
{
struct hda_gen_spec *spec = codec->spec;
bool changed = false;
int i;
if (!spec->power_down_unused || path->active)
if (!(spec->power_down_unused || codec->power_save_node) || path->active)
return;
for (i = 0; i < path->depth; i++) {
hda_nid_t nid = path->path[i];
if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D3) &&
!is_active_nid_for_any(codec, nid)) {
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_POWER_STATE,
AC_PWRST_D3);
changed = true;
}
}
if (changed) {
msleep(10);
snd_hda_codec_read(codec, path->path[0], 0,
AC_VERB_GET_POWER_STATE, 0);
}
sync_power_state_change(codec, path_power_update(codec, path, true));
}
/* turn on/off EAPD on the given pin */
@ -1574,6 +1607,7 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
return 0;
/* print_nid_path(codec, "output-aamix", path); */
path->active = false; /* unused as default */
path->pin_fixed = true; /* static route */
return snd_hda_get_path_idx(codec, path);
}
@ -2998,6 +3032,7 @@ static int new_analog_input(struct hda_codec *codec, int input_idx,
}
path->active = true;
path->stream_enabled = true; /* no DAC/ADC involved */
err = add_loopback_list(spec, mix_nid, idx);
if (err < 0)
return err;
@ -3009,6 +3044,8 @@ static int new_analog_input(struct hda_codec *codec, int input_idx,
if (path) {
print_nid_path(codec, "loopback-merge", path);
path->active = true;
path->pin_fixed = true; /* static route */
path->stream_enabled = true; /* no DAC/ADC involved */
spec->loopback_merge_path =
snd_hda_get_path_idx(codec, path);
}
@ -3810,6 +3847,7 @@ static void parse_digital(struct hda_codec *codec)
continue;
print_nid_path(codec, "digout", path);
path->active = true;
path->pin_fixed = true; /* no jack detection */
spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
set_pin_target(codec, pin, PIN_OUT, false);
if (!nums) {
@ -3837,6 +3875,7 @@ static void parse_digital(struct hda_codec *codec)
if (path) {
print_nid_path(codec, "digin", path);
path->active = true;
path->pin_fixed = true; /* no jack */
spec->dig_in_nid = dig_nid;
spec->digin_path = snd_hda_get_path_idx(codec, path);
set_pin_target(codec, pin, PIN_IN, false);
@ -3896,6 +3935,229 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
return 1;
}
/* power up/down widgets in the all paths that match with the given NID
* as terminals (either start- or endpoint)
*
* returns the last changed NID, or zero if unchanged.
*/
static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid,
int pin_state, int stream_state)
{
struct hda_gen_spec *spec = codec->spec;
hda_nid_t last, changed = 0;
struct nid_path *path;
int n;
for (n = 0; n < spec->paths.used; n++) {
path = snd_array_elem(&spec->paths, n);
if (path->path[0] == nid ||
path->path[path->depth - 1] == nid) {
bool pin_old = path->pin_enabled;
bool stream_old = path->stream_enabled;
if (pin_state >= 0)
path->pin_enabled = pin_state;
if (stream_state >= 0)
path->stream_enabled = stream_state;
if ((!path->pin_fixed && path->pin_enabled != pin_old)
|| path->stream_enabled != stream_old) {
last = path_power_update(codec, path, true);
if (last)
changed = last;
}
}
}
return changed;
}
/* power up/down the paths of the given pin according to the jack state;
* power = 0/1 : only power up/down if it matches with the jack state,
* < 0 : force power up/down to follow the jack sate
*
* returns the last changed NID, or zero if unchanged.
*/
static hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin,
int power)
{
bool on;
if (!codec->power_save_node)
return 0;
on = snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT;
if (power >= 0 && on != power)
return 0;
return set_path_power(codec, pin, on, -1);
}
static void pin_power_callback(struct hda_codec *codec,
struct hda_jack_callback *jack,
bool on)
{
if (jack && jack->tbl->nid)
sync_power_state_change(codec,
set_pin_power_jack(codec, jack->tbl->nid, on));
}
/* callback only doing power up -- called at first */
static void pin_power_up_callback(struct hda_codec *codec,
struct hda_jack_callback *jack)
{
pin_power_callback(codec, jack, true);
}
/* callback only doing power down -- called at last */
static void pin_power_down_callback(struct hda_codec *codec,
struct hda_jack_callback *jack)
{
pin_power_callback(codec, jack, false);
}
/* set up the power up/down callbacks */
static void add_pin_power_ctls(struct hda_codec *codec, int num_pins,
const hda_nid_t *pins, bool on)
{
int i;
hda_jack_callback_fn cb =
on ? pin_power_up_callback : pin_power_down_callback;
for (i = 0; i < num_pins && pins[i]; i++) {
if (is_jack_detectable(codec, pins[i]))
snd_hda_jack_detect_enable_callback(codec, pins[i], cb);
else
set_path_power(codec, pins[i], true, -1);
}
}
/* enabled power callback to each available I/O pin with jack detections;
* the digital I/O pins are excluded because of the unreliable detectsion
*/
static void add_all_pin_power_ctls(struct hda_codec *codec, bool on)
{
struct hda_gen_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
if (!codec->power_save_node)
return;
add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on);
if (cfg->line_out_type != AUTO_PIN_HP_OUT)
add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on);
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on);
for (i = 0; i < cfg->num_inputs; i++)
add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on);
}
/* sync path power up/down with the jack states of given pins */
static void sync_pin_power_ctls(struct hda_codec *codec, int num_pins,
const hda_nid_t *pins)
{
int i;
for (i = 0; i < num_pins && pins[i]; i++)
if (is_jack_detectable(codec, pins[i]))
set_pin_power_jack(codec, pins[i], -1);
}
/* sync path power up/down with pins; called at init and resume */
static void sync_all_pin_power_ctls(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
if (!codec->power_save_node)
return;
sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins);
if (cfg->line_out_type != AUTO_PIN_HP_OUT)
sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins);
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins);
for (i = 0; i < cfg->num_inputs; i++)
sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin);
}
/* add fake paths if not present yet */
static int add_fake_paths(struct hda_codec *codec, hda_nid_t nid,
int num_pins, const hda_nid_t *pins)
{
struct hda_gen_spec *spec = codec->spec;
struct nid_path *path;
int i;
for (i = 0; i < num_pins; i++) {
if (!pins[i])
break;
if (get_nid_path(codec, nid, pins[i], 0))
continue;
path = snd_array_new(&spec->paths);
if (!path)
return -ENOMEM;
memset(path, 0, sizeof(*path));
path->depth = 2;
path->path[0] = nid;
path->path[1] = pins[i];
path->active = true;
}
return 0;
}
/* create fake paths to all outputs from beep */
static int add_fake_beep_paths(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid = spec->beep_nid;
int err;
if (!codec->power_save_node || !nid)
return 0;
err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins);
if (err < 0)
return err;
if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins);
if (err < 0)
return err;
}
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
err = add_fake_paths(codec, nid, cfg->speaker_outs,
cfg->speaker_pins);
if (err < 0)
return err;
}
return 0;
}
/* power up/down beep widget and its output paths */
static void beep_power_hook(struct hda_beep *beep, bool on)
{
set_path_power(beep->codec, beep->nid, -1, on);
}
/**
* snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0
* @codec: the HDA codec
* @pin: NID of pin to fix
*/
int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin)
{
struct hda_gen_spec *spec = codec->spec;
struct nid_path *path;
path = snd_array_new(&spec->paths);
if (!path)
return -ENOMEM;
memset(path, 0, sizeof(*path));
path->depth = 1;
path->path[0] = pin;
path->active = true;
path->pin_fixed = true;
path->stream_enabled = true;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power);
/*
* Jack detections for HP auto-mute and mic-switch
@ -3933,6 +4195,10 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
if (!nid)
break;
oldval = snd_hda_codec_get_pin_target(codec, nid);
if (oldval & PIN_IN)
continue; /* no mute for inputs */
if (spec->auto_mute_via_amp) {
struct nid_path *path;
hda_nid_t mute_nid;
@ -3947,29 +4213,33 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
spec->mute_bits |= (1ULL << mute_nid);
else
spec->mute_bits &= ~(1ULL << mute_nid);
set_pin_eapd(codec, nid, !mute);
continue;
} else {
/* don't reset VREF value in case it's controlling
* the amp (see alc861_fixup_asus_amp_vref_0f())
*/
if (spec->keep_vref_in_automute)
val = oldval & ~PIN_HP;
else
val = 0;
if (!mute)
val |= oldval;
/* here we call update_pin_ctl() so that the pinctl is
* changed without changing the pinctl target value;
* the original target value will be still referred at
* the init / resume again
*/
update_pin_ctl(codec, nid, val);
}
oldval = snd_hda_codec_get_pin_target(codec, nid);
if (oldval & PIN_IN)
continue; /* no mute for inputs */
/* don't reset VREF value in case it's controlling
* the amp (see alc861_fixup_asus_amp_vref_0f())
*/
if (spec->keep_vref_in_automute)
val = oldval & ~PIN_HP;
else
val = 0;
if (!mute)
val |= oldval;
/* here we call update_pin_ctl() so that the pinctl is changed
* without changing the pinctl target value;
* the original target value will be still referred at the
* init / resume again
*/
update_pin_ctl(codec, nid, val);
set_pin_eapd(codec, nid, !mute);
if (codec->power_save_node) {
bool on = !mute;
if (on)
on = snd_hda_jack_detect_state(codec, nid)
!= HDA_JACK_NOT_PRESENT;
set_path_power(codec, nid, on, -1);
}
}
}
@ -4465,6 +4735,21 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix)
}
}
/**
* snd_hda_gen_stream_pm - Stream power management callback
* @codec: the HDA codec
* @nid: audio widget
* @on: power on/off flag
*
* Set this in patch_ops.stream_pm. Only valid with power_save_node flag.
*/
void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on)
{
if (codec->power_save_node)
set_path_power(codec, nid, -1, on);
}
EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm);
/**
* snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and
* set up the hda_gen_spec
@ -4549,6 +4834,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
if (err < 0)
return err;
/* add power-down pin callbacks at first */
add_all_pin_power_ctls(codec, false);
spec->const_channel_count = spec->ext_channel_count;
/* check the multiple speaker and headphone pins */
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
@ -4618,6 +4906,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
}
}
/* add power-up pin callbacks at last */
add_all_pin_power_ctls(codec, true);
/* mute all aamix input initially */
if (spec->mixer_nid)
mute_all_mixer_nid(codec, spec->mixer_nid);
@ -4625,13 +4916,19 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
dig_only:
parse_digital(codec);
if (spec->power_down_unused)
if (spec->power_down_unused || codec->power_save_node)
codec->power_filter = snd_hda_gen_path_power_filter;
if (!spec->no_analog && spec->beep_nid) {
err = snd_hda_attach_beep_device(codec, spec->beep_nid);
if (err < 0)
return err;
if (codec->beep && codec->power_save_node) {
err = add_fake_beep_paths(codec);
if (err < 0)
return err;
codec->beep->power_hook = beep_power_hook;
}
}
return 1;
@ -4675,7 +4972,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
err = snd_hda_create_dig_out_ctls(codec,
spec->multiout.dig_out_nid,
spec->multiout.dig_out_nid,
spec->pcm_rec[1].pcm_type);
spec->pcm_rec[1]->pcm_type);
if (err < 0)
return err;
if (!spec->no_analog) {
@ -5137,6 +5434,33 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
strlcat(str, sfx, len);
}
/* copy PCM stream info from @default_str, and override non-NULL entries
* from @spec_str and @nid
*/
static void setup_pcm_stream(struct hda_pcm_stream *str,
const struct hda_pcm_stream *default_str,
const struct hda_pcm_stream *spec_str,
hda_nid_t nid)
{
*str = *default_str;
if (nid)
str->nid = nid;
if (spec_str) {
if (spec_str->substreams)
str->substreams = spec_str->substreams;
if (spec_str->channels_min)
str->channels_min = spec_str->channels_min;
if (spec_str->channels_max)
str->channels_max = spec_str->channels_max;
if (spec_str->rates)
str->rates = spec_str->rates;
if (spec_str->formats)
str->formats = spec_str->formats;
if (spec_str->maxbps)
str->maxbps = spec_str->maxbps;
}
}
/**
* snd_hda_gen_build_pcms - build PCM streams based on the parsed results
* @codec: the HDA codec
@ -5146,27 +5470,25 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
int snd_hda_gen_build_pcms(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec;
const struct hda_pcm_stream *p;
struct hda_pcm *info;
bool have_multi_adcs;
codec->num_pcms = 1;
codec->pcm_info = info;
if (spec->no_analog)
goto skip_analog;
fill_pcm_stream_name(spec->stream_name_analog,
sizeof(spec->stream_name_analog),
" Analog", codec->chip_name);
info->name = spec->stream_name_analog;
info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog);
if (!info)
return -ENOMEM;
spec->pcm_rec[0] = info;
if (spec->multiout.num_dacs > 0) {
p = spec->stream_analog_playback;
if (!p)
p = &pcm_analog_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
&pcm_analog_playback,
spec->stream_analog_playback,
spec->multiout.dac_nids[0]);
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
spec->multiout.max_channels;
if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
@ -5175,15 +5497,11 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
snd_pcm_2_1_chmaps;
}
if (spec->num_adc_nids) {
p = spec->stream_analog_capture;
if (!p) {
if (spec->dyn_adc_switch)
p = &dyn_adc_pcm_analog_capture;
else
p = &pcm_analog_capture;
}
info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
(spec->dyn_adc_switch ?
&dyn_adc_pcm_analog_capture : &pcm_analog_capture),
spec->stream_analog_capture,
spec->adc_nids[0]);
}
skip_analog:
@ -5192,28 +5510,26 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
fill_pcm_stream_name(spec->stream_name_digital,
sizeof(spec->stream_name_digital),
" Digital", codec->chip_name);
codec->num_pcms = 2;
info = snd_hda_codec_pcm_new(codec, "%s",
spec->stream_name_digital);
if (!info)
return -ENOMEM;
codec->slave_dig_outs = spec->multiout.slave_dig_outs;
info = spec->pcm_rec + 1;
info->name = spec->stream_name_digital;
spec->pcm_rec[1] = info;
if (spec->dig_out_type)
info->pcm_type = spec->dig_out_type;
else
info->pcm_type = HDA_PCM_TYPE_SPDIF;
if (spec->multiout.dig_out_nid) {
p = spec->stream_digital_playback;
if (!p)
p = &pcm_digital_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
}
if (spec->dig_in_nid) {
p = spec->stream_digital_capture;
if (!p)
p = &pcm_digital_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
}
if (spec->multiout.dig_out_nid)
setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
&pcm_digital_playback,
spec->stream_digital_playback,
spec->multiout.dig_out_nid);
if (spec->dig_in_nid)
setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
&pcm_digital_capture,
spec->stream_digital_capture,
spec->dig_in_nid);
}
if (spec->no_analog)
@ -5229,34 +5545,29 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
fill_pcm_stream_name(spec->stream_name_alt_analog,
sizeof(spec->stream_name_alt_analog),
" Alt Analog", codec->chip_name);
codec->num_pcms = 3;
info = spec->pcm_rec + 2;
info->name = spec->stream_name_alt_analog;
if (spec->alt_dac_nid) {
p = spec->stream_analog_alt_playback;
if (!p)
p = &pcm_analog_alt_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
spec->alt_dac_nid;
} else {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
pcm_null_stream;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
}
info = snd_hda_codec_pcm_new(codec, "%s",
spec->stream_name_alt_analog);
if (!info)
return -ENOMEM;
spec->pcm_rec[2] = info;
if (spec->alt_dac_nid)
setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
&pcm_analog_alt_playback,
spec->stream_analog_alt_playback,
spec->alt_dac_nid);
else
setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
&pcm_null_stream, NULL, 0);
if (have_multi_adcs) {
p = spec->stream_analog_alt_capture;
if (!p)
p = &pcm_analog_alt_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
spec->adc_nids[1];
setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
&pcm_analog_alt_capture,
spec->stream_analog_alt_capture,
spec->adc_nids[1]);
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
spec->num_adc_nids - 1;
} else {
info->stream[SNDRV_PCM_STREAM_CAPTURE] =
pcm_null_stream;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
&pcm_null_stream, NULL, 0);
}
}
@ -5464,6 +5775,8 @@ int snd_hda_gen_init(struct hda_codec *codec)
clear_unsol_on_unused_pins(codec);
sync_all_pin_power_ctls(codec);
/* call init functions of standard auto-mute helpers */
update_automute_all(codec);
@ -5524,13 +5837,11 @@ static const struct hda_codec_ops generic_patch_ops = {
#endif
};
/**
/*
* snd_hda_parse_generic_codec - Generic codec parser
* @codec: the HDA codec
*
* This should be called from the HDA codec core.
*/
int snd_hda_parse_generic_codec(struct hda_codec *codec)
static int snd_hda_parse_generic_codec(struct hda_codec *codec)
{
struct hda_gen_spec *spec;
int err;
@ -5556,7 +5867,17 @@ error:
snd_hda_gen_free(codec);
return err;
}
EXPORT_SYMBOL_GPL(snd_hda_parse_generic_codec);
static const struct hda_codec_preset snd_hda_preset_generic[] = {
{ .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
{} /* terminator */
};
static struct hda_codec_driver generic_driver = {
.preset = snd_hda_preset_generic,
};
module_hda_codec_driver(generic_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic HD-audio codec parser");

View file

@ -46,7 +46,10 @@ struct nid_path {
unsigned char idx[MAX_NID_PATH_DEPTH];
unsigned char multi[MAX_NID_PATH_DEPTH];
unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
bool active;
bool active:1; /* activated by driver */
bool pin_enabled:1; /* pins are enabled */
bool pin_fixed:1; /* path with fixed pin */
bool stream_enabled:1; /* stream is active */
};
/* mic/line-in auto switching entry */
@ -144,7 +147,7 @@ struct hda_gen_spec {
int const_channel_count; /* channel count for all */
/* PCM information */
struct hda_pcm pcm_rec[3]; /* used in build_pcms() */
struct hda_pcm *pcm_rec[3]; /* used in build_pcms() */
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
@ -340,5 +343,7 @@ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid);
unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
hda_nid_t nid,
unsigned int power_state);
void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on);
int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin);
#endif /* __SOUND_HDA_GENERIC_H */

View file

@ -101,7 +101,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec)
int err;
sprintf(hwname, "HDA Codec %d", codec->addr);
err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep);
err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep);
if (err < 0)
return err;
codec->hwdep = hwdep;
@ -116,9 +116,6 @@ int snd_hda_create_hwdep(struct hda_codec *codec)
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif
/* link to codec */
hwdep->dev.parent = &codec->dev;
/* for sysfs */
hwdep->dev.groups = snd_hda_dev_attr_groups;
dev_set_drvdata(&hwdep->dev, codec);

View file

@ -22,7 +22,7 @@
#include <linux/component.h>
#include <drm/i915_component.h>
#include <sound/core.h>
#include "hda_priv.h"
#include "hda_controller.h"
#include "hda_intel.h"
/* Intel HSW/BDW display HDA controller Extended Mode registers.

View file

@ -62,7 +62,6 @@
#include <linux/firmware.h>
#include "hda_codec.h"
#include "hda_controller.h"
#include "hda_priv.h"
#include "hda_intel.h"
/* position fix mode */
@ -174,7 +173,6 @@ static struct kernel_param_ops param_ops_xint = {
#define param_check_xint param_check_int
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
static int *power_save_addr = &power_save;
module_param(power_save, xint, 0644);
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
"(in second, 0 = disable).");
@ -187,7 +185,7 @@ static bool power_save_controller = 1;
module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
#else
static int *power_save_addr;
#define power_save 0
#endif /* CONFIG_PM */
static int align_buffer_size = -1;
@ -530,10 +528,10 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
if (ok == 1) {
azx_dev->irq_pending = 0;
return ok;
} else if (ok == 0 && chip->bus && chip->bus->workq) {
} else if (ok == 0) {
/* bogus IRQ, process it later */
azx_dev->irq_pending = 1;
queue_work(chip->bus->workq, &hda->irq_pending_work);
schedule_work(&hda->irq_pending_work);
}
return 0;
}
@ -741,7 +739,6 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
{
struct hda_intel *hda;
struct azx *chip;
struct hda_codec *c;
int prev = power_save;
int ret = param_set_int(val, kp);
@ -753,8 +750,7 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
chip = &hda->chip;
if (!chip->bus || chip->disabled)
continue;
list_for_each_entry(c, &chip->bus->codec_list, list)
snd_hda_power_sync(c);
snd_hda_set_power_save(chip->bus, power_save * 1000);
}
mutex_unlock(&card_list_lock);
return 0;
@ -773,7 +769,6 @@ static int azx_suspend(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
struct hda_intel *hda;
struct azx_pcm *p;
if (!card)
return 0;
@ -785,10 +780,6 @@ static int azx_suspend(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
azx_clear_irq_pending(chip);
list_for_each_entry(p, &chip->pcm_list, list)
snd_pcm_suspend_all(p->pcm);
if (chip->initialized)
snd_hda_suspend(chip->bus);
azx_stop_chip(chip);
azx_enter_link_reset(chip);
if (chip->irq >= 0) {
@ -831,7 +822,6 @@ static int azx_resume(struct device *dev)
azx_init_chip(chip, true);
snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
@ -852,7 +842,7 @@ static int azx_runtime_suspend(struct device *dev)
if (chip->disabled || hda->init_failed)
return 0;
if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
if (!azx_has_pm_runtime(chip))
return 0;
/* enable controller wake up event */
@ -885,7 +875,7 @@ static int azx_runtime_resume(struct device *dev)
if (chip->disabled || hda->init_failed)
return 0;
if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
if (!azx_has_pm_runtime(chip))
return 0;
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
@ -903,8 +893,8 @@ static int azx_runtime_resume(struct device *dev)
if (status && bus) {
list_for_each_entry(codec, &bus->codec_list, list)
if (status & (1 << codec->addr))
queue_delayed_work(codec->bus->workq,
&codec->jackpoll_work, codec->jackpoll_interval);
schedule_delayed_work(&codec->jackpoll_work,
codec->jackpoll_interval);
}
/* disable controller Wake Up event*/
@ -928,8 +918,8 @@ static int azx_runtime_idle(struct device *dev)
if (chip->disabled || hda->init_failed)
return 0;
if (!power_save_controller ||
!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
if (!power_save_controller || !azx_has_pm_runtime(chip) ||
chip->bus->codec_powered)
return -EBUSY;
return 0;
@ -1071,14 +1061,11 @@ static int azx_free(struct azx *chip)
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
int i;
if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
&& chip->running)
if (azx_has_pm_runtime(chip) && chip->running)
pm_runtime_get_noresume(&pci->dev);
azx_del_card_list(chip);
azx_notifier_unregister(chip);
hda->init_failed = 1; /* to be sure */
complete_all(&hda->probe_wait);
@ -1394,7 +1381,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
hda = kzalloc(sizeof(*hda), GFP_KERNEL);
if (!hda) {
dev_err(card->dev, "Cannot allocate hda\n");
pci_disable_device(pci);
return -ENOMEM;
}
@ -1575,10 +1561,8 @@ static int azx_first_init(struct azx *chip)
chip->num_streams = chip->playback_streams + chip->capture_streams;
chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
GFP_KERNEL);
if (!chip->azx_dev) {
dev_err(card->dev, "cannot malloc azx_dev\n");
if (!chip->azx_dev)
return -ENOMEM;
}
err = azx_alloc_stream_pages(chip);
if (err < 0)
@ -1615,19 +1599,6 @@ static int azx_first_init(struct azx *chip)
return 0;
}
static void power_down_all_codecs(struct azx *chip)
{
#ifdef CONFIG_PM
/* The codecs were powered up in snd_hda_codec_new().
* Now all initialization done, so turn them down if possible
*/
struct hda_codec *codec;
list_for_each_entry(codec, &chip->bus->codec_list, list) {
snd_hda_power_down(codec);
}
#endif
}
#ifdef CONFIG_SND_HDA_PATCH_LOADER
/* callback from request_firmware_nowait() */
static void azx_firmware_cb(const struct firmware *fw, void *context)
@ -1896,12 +1867,14 @@ static int azx_probe_continue(struct azx *chip)
#endif
/* create codec instances */
err = azx_codec_create(chip, model[dev],
azx_max_codecs[chip->driver_type],
power_save_addr);
err = azx_bus_create(chip, model[dev]);
if (err < 0)
goto out_free;
err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
if (err < 0)
goto out_free;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
if (chip->fw) {
err = snd_hda_load_patch(chip->bus, chip->fw->size,
@ -1920,25 +1893,14 @@ static int azx_probe_continue(struct azx *chip)
goto out_free;
}
/* create PCM streams */
err = snd_hda_build_pcms(chip->bus);
if (err < 0)
goto out_free;
/* create mixer controls */
err = azx_mixer_create(chip);
if (err < 0)
goto out_free;
err = snd_card_register(chip->card);
if (err < 0)
goto out_free;
chip->running = 1;
power_down_all_codecs(chip);
azx_notifier_register(chip);
azx_add_card_list(chip);
if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME) || hda->use_vga_switcheroo)
snd_hda_set_power_save(chip->bus, power_save * 1000);
if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
pm_runtime_put_noidle(&pci->dev);
out_free:
@ -1956,6 +1918,18 @@ static void azx_remove(struct pci_dev *pci)
snd_card_free(card);
}
static void azx_shutdown(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
struct azx *chip;
if (!card)
return;
chip = card->private_data;
if (chip && chip->running)
azx_stop_chip(chip);
}
/* PCI IDs */
static const struct pci_device_id azx_ids[] = {
/* CPT */
@ -2178,6 +2152,7 @@ static struct pci_driver azx_driver = {
.id_table = azx_ids,
.probe = azx_probe,
.remove = azx_remove,
.shutdown = azx_shutdown,
.driver = {
.pm = AZX_PM_OPS,
},

View file

@ -17,7 +17,7 @@
#define __SOUND_HDA_INTEL_H
#include <drm/i915_component.h>
#include "hda_priv.h"
#include "hda_controller.h"
struct hda_intel {
struct azx chip;

View file

@ -135,7 +135,7 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_INPUT_JACK
/* free jack instances manually when clearing/reconfiguring */
if (!codec->bus->shutdown && jack->jack)
snd_device_free(codec->bus->card, jack->jack);
snd_device_free(codec->card, jack->jack);
#endif
for (cb = jack->callback; cb; cb = next) {
next = cb->next;
@ -340,7 +340,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec)
if (!jack->kctl || jack->block_report)
continue;
state = get_jack_plug_state(jack->pin_sense);
snd_kctl_jack_report(codec->bus->card, jack->kctl, state);
snd_kctl_jack_report(codec->card, jack->kctl, state);
#ifdef CONFIG_SND_HDA_INPUT_JACK
if (jack->jack)
snd_jack_report(jack->jack,
@ -412,11 +412,11 @@ static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
jack->phantom_jack = !!phantom_jack;
state = snd_hda_jack_detect(codec, nid);
snd_kctl_jack_report(codec->bus->card, kctl, state);
snd_kctl_jack_report(codec->card, kctl, state);
#ifdef CONFIG_SND_HDA_INPUT_JACK
if (!phantom_jack) {
jack->type = get_input_jack_type(codec, nid);
err = snd_jack_new(codec->bus->card, name, jack->type,
err = snd_jack_new(codec->card, name, jack->type,
&jack->jack);
if (err < 0)
return err;

View file

@ -150,6 +150,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
#define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \
__snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL)
int snd_hda_codec_reset(struct hda_codec *codec);
void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
enum {
HDA_VMUTE_OFF,
@ -272,29 +273,6 @@ int snd_hda_add_imux_item(struct hda_codec *codec,
struct hda_input_mux *imux, const char *label,
int index, int *type_index_ret);
/*
* Channel mode helper
*/
struct hda_channel_mode {
int channels;
const struct hda_verb *sequence;
};
int snd_hda_ch_mode_info(struct hda_codec *codec,
struct snd_ctl_elem_info *uinfo,
const struct hda_channel_mode *chmode,
int num_chmodes);
int snd_hda_ch_mode_get(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
const struct hda_channel_mode *chmode,
int num_chmodes,
int max_channels);
int snd_hda_ch_mode_put(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
const struct hda_channel_mode *chmode,
int num_chmodes,
int *max_channelsp);
/*
* Multi-channel / digital-out PCM helper
*/
@ -350,12 +328,6 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout);
/*
* generic codec parser
*/
int snd_hda_parse_generic_codec(struct hda_codec *codec);
int snd_hda_parse_hdmi_codec(struct hda_codec *codec);
/*
* generic proc interface
*/
@ -466,23 +438,6 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec,
const struct snd_hda_pin_quirk *pin_quirk,
const struct hda_fixup *fixlist);
/*
* unsolicited event handler
*/
#define HDA_UNSOL_QUEUE_SIZE 64
struct hda_bus_unsolicited {
/* ring buffer */
u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
unsigned int rp, wp;
/* workqueue */
struct work_struct work;
struct hda_bus *bus;
};
/* helper macros to retrieve pin default-config values */
#define get_defcfg_connect(cfg) \
((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
@ -800,9 +755,13 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
/*
*/
#define codec_err(codec, fmt, args...) dev_err(&(codec)->dev, fmt, ##args)
#define codec_warn(codec, fmt, args...) dev_warn(&(codec)->dev, fmt, ##args)
#define codec_info(codec, fmt, args...) dev_info(&(codec)->dev, fmt, ##args)
#define codec_dbg(codec, fmt, args...) dev_dbg(&(codec)->dev, fmt, ##args)
#define codec_err(codec, fmt, args...) \
dev_err(hda_codec_dev(codec), fmt, ##args)
#define codec_warn(codec, fmt, args...) \
dev_warn(hda_codec_dev(codec), fmt, ##args)
#define codec_info(codec, fmt, args...) \
dev_info(hda_codec_dev(codec), fmt, ##args)
#define codec_dbg(codec, fmt, args...) \
dev_dbg(hda_codec_dev(codec), fmt, ##args)
#endif /* __SOUND_HDA_LOCAL_H */

View file

@ -1,406 +0,0 @@
/*
* Common defines for the alsa driver code base for HD Audio.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __SOUND_HDA_PRIV_H
#define __SOUND_HDA_PRIV_H
#include <linux/timecounter.h>
#include <sound/core.h>
#include <sound/pcm.h>
/*
* registers
*/
#define AZX_REG_GCAP 0x00
#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */
#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */
#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */
#define AZX_GCAP_ISS (15 << 8) /* # of input streams */
#define AZX_GCAP_OSS (15 << 12) /* # of output streams */
#define AZX_REG_VMIN 0x02
#define AZX_REG_VMAJ 0x03
#define AZX_REG_OUTPAY 0x04
#define AZX_REG_INPAY 0x06
#define AZX_REG_GCTL 0x08
#define AZX_GCTL_RESET (1 << 0) /* controller reset */
#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */
#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
#define AZX_REG_WAKEEN 0x0c
#define AZX_REG_STATESTS 0x0e
#define AZX_REG_GSTS 0x10
#define AZX_GSTS_FSTS (1 << 1) /* flush status */
#define AZX_REG_INTCTL 0x20
#define AZX_REG_INTSTS 0x24
#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */
#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */
#define AZX_REG_SSYNC 0x38
#define AZX_REG_CORBLBASE 0x40
#define AZX_REG_CORBUBASE 0x44
#define AZX_REG_CORBWP 0x48
#define AZX_REG_CORBRP 0x4a
#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */
#define AZX_REG_CORBCTL 0x4c
#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */
#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
#define AZX_REG_CORBSTS 0x4d
#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */
#define AZX_REG_CORBSIZE 0x4e
#define AZX_REG_RIRBLBASE 0x50
#define AZX_REG_RIRBUBASE 0x54
#define AZX_REG_RIRBWP 0x58
#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */
#define AZX_REG_RINTCNT 0x5a
#define AZX_REG_RIRBCTL 0x5c
#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */
#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
#define AZX_REG_RIRBSTS 0x5d
#define AZX_RBSTS_IRQ (1 << 0) /* response irq */
#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */
#define AZX_REG_RIRBSIZE 0x5e
#define AZX_REG_IC 0x60
#define AZX_REG_IR 0x64
#define AZX_REG_IRS 0x68
#define AZX_IRS_VALID (1<<1)
#define AZX_IRS_BUSY (1<<0)
#define AZX_REG_DPLBASE 0x70
#define AZX_REG_DPUBASE 0x74
#define AZX_DPLBASE_ENABLE 0x1 /* Enable position buffer */
/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* stream register offsets from stream base */
#define AZX_REG_SD_CTL 0x00
#define AZX_REG_SD_STS 0x03
#define AZX_REG_SD_LPIB 0x04
#define AZX_REG_SD_CBL 0x08
#define AZX_REG_SD_LVI 0x0c
#define AZX_REG_SD_FIFOW 0x0e
#define AZX_REG_SD_FIFOSIZE 0x10
#define AZX_REG_SD_FORMAT 0x12
#define AZX_REG_SD_BDLPL 0x18
#define AZX_REG_SD_BDLPU 0x1c
/* PCI space */
#define AZX_PCIREG_TCSEL 0x44
/*
* other constants
*/
/* max number of fragments - we may use more if allocating more pages for BDL */
#define BDL_SIZE 4096
#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
#define AZX_MAX_FRAG 32
/* max buffer size - no h/w limit, you can increase as you like */
#define AZX_MAX_BUF_SIZE (1024*1024*1024)
/* RIRB int mask: overrun[2], response[0] */
#define RIRB_INT_RESPONSE 0x01
#define RIRB_INT_OVERRUN 0x04
#define RIRB_INT_MASK 0x05
/* STATESTS int mask: S3,SD2,SD1,SD0 */
#define AZX_MAX_CODECS 8
#define AZX_DEFAULT_CODECS 4
#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
#define SD_CTL_STRIPE (3 << 16) /* stripe control */
#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
#define SD_CTL_STREAM_TAG_SHIFT 20
/* SD_CTL and SD_STS */
#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
#define SD_INT_COMPLETE 0x04 /* completion interrupt */
#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
SD_INT_COMPLETE)
/* SD_STS */
#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
/* INTCTL and INTSTS */
#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */
#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
/* below are so far hardcoded - should read registers in future */
#define AZX_MAX_CORB_ENTRIES 256
#define AZX_MAX_RIRB_ENTRIES 256
/* driver quirks (capabilities) */
/* bits 0-7 are used for indicating driver type */
#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */
#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */
#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */
#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */
#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */
#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */
#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */
#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */
#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */
#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */
#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */
/* 22 unused */
#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
#define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */
#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */
#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */
enum {
AZX_SNOOP_TYPE_NONE ,
AZX_SNOOP_TYPE_SCH,
AZX_SNOOP_TYPE_ATI,
AZX_SNOOP_TYPE_NVIDIA,
};
/* HD Audio class code */
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
struct azx_dev {
struct snd_dma_buffer bdl; /* BDL buffer */
u32 *posbuf; /* position buffer pointer */
unsigned int bufsize; /* size of the play buffer in bytes */
unsigned int period_bytes; /* size of the period in bytes */
unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */
unsigned long start_wallclk; /* start + minimum wallclk */
unsigned long period_wallclk; /* wallclk for period */
void __iomem *sd_addr; /* stream descriptor pointer */
u32 sd_int_sta_mask; /* stream int status mask */
/* pcm support */
struct snd_pcm_substream *substream; /* assigned substream,
* set in PCM open
*/
unsigned int format_val; /* format value to be set in the
* controller and the codec
*/
unsigned char stream_tag; /* assigned stream */
unsigned char index; /* stream index */
int assigned_key; /* last device# key assigned to */
unsigned int opened:1;
unsigned int running:1;
unsigned int irq_pending:1;
unsigned int prepared:1;
unsigned int locked:1;
/*
* For VIA:
* A flag to ensure DMA position is 0
* when link position is not greater than FIFO size
*/
unsigned int insufficient:1;
unsigned int wc_marked:1;
unsigned int no_period_wakeup:1;
struct timecounter azx_tc;
struct cyclecounter azx_cc;
int delay_negative_threshold;
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* Allows dsp load to have sole access to the playback stream. */
struct mutex dsp_mutex;
#endif
};
/* CORB/RIRB */
struct azx_rb {
u32 *buf; /* CORB/RIRB buffer
* Each CORB entry is 4byte, RIRB is 8byte
*/
dma_addr_t addr; /* physical address of CORB/RIRB buffer */
/* for RIRB */
unsigned short rp, wp; /* read/write pointers */
int cmds[AZX_MAX_CODECS]; /* number of pending requests */
u32 res[AZX_MAX_CODECS]; /* last read value */
};
struct azx;
/* Functions to read/write to hda registers. */
struct hda_controller_ops {
/* Register Access */
void (*reg_writel)(u32 value, u32 __iomem *addr);
u32 (*reg_readl)(u32 __iomem *addr);
void (*reg_writew)(u16 value, u16 __iomem *addr);
u16 (*reg_readw)(u16 __iomem *addr);
void (*reg_writeb)(u8 value, u8 __iomem *addr);
u8 (*reg_readb)(u8 __iomem *addr);
/* Disable msi if supported, PCI only */
int (*disable_msi_reset_irq)(struct azx *);
/* Allocation ops */
int (*dma_alloc_pages)(struct azx *chip,
int type,
size_t size,
struct snd_dma_buffer *buf);
void (*dma_free_pages)(struct azx *chip, struct snd_dma_buffer *buf);
int (*substream_alloc_pages)(struct azx *chip,
struct snd_pcm_substream *substream,
size_t size);
int (*substream_free_pages)(struct azx *chip,
struct snd_pcm_substream *substream);
void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream,
struct vm_area_struct *area);
/* Check if current position is acceptable */
int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
};
struct azx_pcm {
struct azx *chip;
struct snd_pcm *pcm;
struct hda_codec *codec;
struct hda_pcm_stream *hinfo[2];
struct list_head list;
};
typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *);
typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos);
struct azx {
struct snd_card *card;
struct pci_dev *pci;
int dev_index;
/* chip type specific */
int driver_type;
unsigned int driver_caps;
int playback_streams;
int playback_index_offset;
int capture_streams;
int capture_index_offset;
int num_streams;
const int *jackpoll_ms; /* per-card jack poll interval */
/* Register interaction. */
const struct hda_controller_ops *ops;
/* position adjustment callbacks */
azx_get_pos_callback_t get_position[2];
azx_get_delay_callback_t get_delay[2];
/* pci resources */
unsigned long addr;
void __iomem *remap_addr;
int irq;
/* locks */
spinlock_t reg_lock;
struct mutex open_mutex; /* Prevents concurrent open/close operations */
/* streams (x num_streams) */
struct azx_dev *azx_dev;
/* PCM */
struct list_head pcm_list; /* azx_pcm list */
/* HD codec */
unsigned short codec_mask;
int codec_probe_mask; /* copied from probe_mask option */
struct hda_bus *bus;
unsigned int beep_mode;
/* CORB/RIRB */
struct azx_rb corb;
struct azx_rb rirb;
/* CORB/RIRB and position buffers */
struct snd_dma_buffer rb;
struct snd_dma_buffer posbuf;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
const struct firmware *fw;
#endif
/* flags */
const int *bdl_pos_adj;
int poll_count;
unsigned int running:1;
unsigned int initialized:1;
unsigned int single_cmd:1;
unsigned int polling_mode:1;
unsigned int msi:1;
unsigned int probing:1; /* codec probing phase */
unsigned int snoop:1;
unsigned int align_buffer_size:1;
unsigned int region_requested:1;
unsigned int disabled:1; /* disabled by VGA-switcher */
/* for debugging */
unsigned int last_cmd[AZX_MAX_CODECS];
/* reboot notifier (for mysterious hangup problem at power-down) */
struct notifier_block reboot_notifier;
#ifdef CONFIG_SND_HDA_DSP_LOADER
struct azx_dev saved_azx_dev;
#endif
};
#ifdef CONFIG_X86
#define azx_snoop(chip) ((chip)->snoop)
#else
#define azx_snoop(chip) true
#endif
/*
* macros for easy use
*/
#define azx_writel(chip, reg, value) \
((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg))
#define azx_readl(chip, reg) \
((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg))
#define azx_writew(chip, reg, value) \
((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg))
#define azx_readw(chip, reg) \
((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg))
#define azx_writeb(chip, reg, value) \
((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg))
#define azx_readb(chip, reg) \
((chip)->ops->reg_readb((chip)->remap_addr + AZX_REG_##reg))
#define azx_sd_writel(chip, dev, reg, value) \
((chip)->ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readl(chip, dev, reg) \
((chip)->ops->reg_readl((dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_writew(chip, dev, reg, value) \
((chip)->ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readw(chip, dev, reg) \
((chip)->ops->reg_readw((dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_writeb(chip, dev, reg, value) \
((chip)->ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readb(chip, dev, reg) \
((chip)->ops->reg_readb((dev)->sd_addr + AZX_REG_##reg))
#endif /* __SOUND_HDA_PRIV_H */

View file

@ -99,10 +99,10 @@ static void print_nid_array(struct snd_info_buffer *buffer,
static void print_nid_pcms(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
int pcm, type;
int type;
struct hda_pcm *cpcm;
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
cpcm = &codec->pcm_info[pcm];
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
for (type = 0; type < 2; type++) {
if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
continue;
@ -861,7 +861,7 @@ int snd_hda_codec_proc_new(struct hda_codec *codec)
int err;
snprintf(name, sizeof(name), "codec#%d", codec->addr);
err = snd_card_proc_new(codec->bus->card, name, &entry);
err = snd_card_proc_new(codec->card, name, &entry);
if (err < 0)
return err;

View file

@ -149,7 +149,7 @@ static int reconfig_codec(struct hda_codec *codec)
err = snd_hda_codec_build_controls(codec);
if (err < 0)
goto error;
err = snd_card_register(codec->bus->card);
err = snd_card_register(codec->card);
error:
snd_hda_power_down(codec);
return err;

View file

@ -37,7 +37,6 @@
#include "hda_codec.h"
#include "hda_controller.h"
#include "hda_priv.h"
/* Defines for Nvidia Tegra HDA support */
#define HDA_BAR0 0x8000
@ -82,7 +81,7 @@ module_param(power_save, bint, 0644);
MODULE_PARM_DESC(power_save,
"Automatic power-saving timeout (in seconds, 0 = disable).");
#else
static int power_save = 0;
#define power_save 0
#endif
/*
@ -250,14 +249,9 @@ static int hda_tegra_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
struct azx_pcm *p;
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
list_for_each_entry(p, &chip->pcm_list, list)
snd_pcm_suspend_all(p->pcm);
if (chip->initialized)
snd_hda_suspend(chip->bus);
azx_stop_chip(chip);
azx_enter_link_reset(chip);
@ -278,7 +272,6 @@ static int hda_tegra_resume(struct device *dev)
azx_init_chip(chip, 1);
snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
@ -297,8 +290,6 @@ static int hda_tegra_dev_free(struct snd_device *device)
int i;
struct azx *chip = device->device_data;
azx_notifier_unregister(chip);
if (chip->initialized) {
for (i = 0; i < chip->num_streams; i++)
azx_stream_stop(chip, &chip->azx_dev[i]);
@ -344,17 +335,6 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
return 0;
}
/*
* The codecs were powered up in snd_hda_codec_new().
* Now all initialization done, so turn them down if possible
*/
static void power_down_all_codecs(struct azx *chip)
{
struct hda_codec *codec;
list_for_each_entry(codec, &chip->bus->codec_list, list)
snd_hda_power_down(codec);
}
static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
{
struct snd_card *card = chip->card;
@ -503,7 +483,11 @@ static int hda_tegra_probe(struct platform_device *pdev)
goto out_free;
/* create codec instances */
err = azx_codec_create(chip, NULL, 0, &power_save);
err = azx_bus_create(chip, NULL);
if (err < 0)
goto out_free;
err = azx_probe_codecs(chip, 0);
if (err < 0)
goto out_free;
@ -511,23 +495,12 @@ static int hda_tegra_probe(struct platform_device *pdev)
if (err < 0)
goto out_free;
/* create PCM streams */
err = snd_hda_build_pcms(chip->bus);
if (err < 0)
goto out_free;
/* create mixer controls */
err = azx_mixer_create(chip);
if (err < 0)
goto out_free;
err = snd_card_register(chip->card);
if (err < 0)
goto out_free;
chip->running = 1;
power_down_all_codecs(chip);
azx_notifier_register(chip);
snd_hda_set_power_save(chip->bus, power_save * 1000);
return 0;
@ -541,6 +514,18 @@ static int hda_tegra_remove(struct platform_device *pdev)
return snd_card_free(dev_get_drvdata(&pdev->dev));
}
static void hda_tegra_shutdown(struct platform_device *pdev)
{
struct snd_card *card = dev_get_drvdata(&pdev->dev);
struct azx *chip;
if (!card)
return;
chip = card->private_data;
if (chip && chip->running)
azx_stop_chip(chip);
}
static struct platform_driver tegra_platform_hda = {
.driver = {
.name = "tegra-hda",
@ -549,6 +534,7 @@ static struct platform_driver tegra_platform_hda = {
},
.probe = hda_tegra_probe,
.remove = hda_tegra_remove,
.shutdown = hda_tegra_shutdown,
};
module_platform_driver(tegra_platform_hda);

View file

@ -23,7 +23,7 @@ DECLARE_EVENT_CLASS(hda_cmd,
),
TP_fast_assign(
__entry->card = (codec)->bus->card->number;
__entry->card = (codec)->card->number;
__entry->addr = (codec)->addr;
__entry->val = (val);
),
@ -71,7 +71,7 @@ DECLARE_EVENT_CLASS(hda_power,
),
TP_fast_assign(
__entry->card = (codec)->bus->card->number;
__entry->card = (codec)->card->number;
__entry->addr = (codec)->addr;
),
@ -87,30 +87,6 @@ DEFINE_EVENT(hda_power, hda_power_up,
TP_PROTO(struct hda_codec *codec),
TP_ARGS(codec)
);
TRACE_EVENT(hda_power_count,
TP_PROTO(struct hda_codec *codec),
TP_ARGS(codec),
TP_STRUCT__entry(
__field( unsigned int, card )
__field( unsigned int, addr )
__field( int, power_count )
__field( int, power_on )
__field( int, power_transition )
),
TP_fast_assign(
__entry->card = (codec)->bus->card->number;
__entry->addr = (codec)->addr;
__entry->power_count = (codec)->power_count;
__entry->power_on = (codec)->power_on;
__entry->power_transition = (codec)->power_transition;
),
TP_printk("[%d:%d] power_count=%d, power_on=%d, power_transition=%d",
__entry->card, __entry->addr, __entry->power_count,
__entry->power_on, __entry->power_transition)
);
#endif /* CONFIG_PM */
TRACE_EVENT(hda_unsol_event,

View file

@ -1194,20 +1194,8 @@ MODULE_ALIAS("snd-hda-codec-id:11d4*");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Analog Devices HD-audio codec");
static struct hda_codec_preset_list analog_list = {
static struct hda_codec_driver analog_driver = {
.preset = snd_hda_preset_analog,
.owner = THIS_MODULE,
};
static int __init patch_analog_init(void)
{
return snd_hda_add_codec_preset(&analog_list);
}
static void __exit patch_analog_exit(void)
{
snd_hda_delete_codec_preset(&analog_list);
}
module_init(patch_analog_init)
module_exit(patch_analog_exit)
module_hda_codec_driver(analog_driver);

View file

@ -98,20 +98,8 @@ MODULE_ALIAS("snd-hda-codec-id:1102000d");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
static struct hda_codec_preset_list ca0110_list = {
static struct hda_codec_driver ca0110_driver = {
.preset = snd_hda_preset_ca0110,
.owner = THIS_MODULE,
};
static int __init patch_ca0110_init(void)
{
return snd_hda_add_codec_preset(&ca0110_list);
}
static void __exit patch_ca0110_exit(void)
{
snd_hda_delete_codec_preset(&ca0110_list);
}
module_init(patch_ca0110_init)
module_exit(patch_ca0110_exit)
module_hda_codec_driver(ca0110_driver);

View file

@ -719,7 +719,6 @@ struct ca0132_spec {
unsigned int num_inputs;
hda_nid_t shared_mic_nid;
hda_nid_t shared_out_nid;
struct hda_pcm pcm_rec[5]; /* PCM information */
/* chip access */
struct mutex chipio_mutex; /* chip access mutex */
@ -4036,12 +4035,11 @@ static struct hda_pcm_stream ca0132_pcm_digital_capture = {
static int ca0132_build_pcms(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec;
struct hda_pcm *info;
codec->pcm_info = info;
codec->num_pcms = 0;
info->name = "CA0132 Analog";
info = snd_hda_codec_pcm_new(codec, "CA0132 Analog");
if (!info)
return -ENOMEM;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
@ -4049,27 +4047,27 @@ static int ca0132_build_pcms(struct hda_codec *codec)
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
codec->num_pcms++;
info++;
info->name = "CA0132 Analog Mic-In2";
info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2");
if (!info)
return -ENOMEM;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
codec->num_pcms++;
info++;
info->name = "CA0132 What U Hear";
info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear");
if (!info)
return -ENOMEM;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2];
codec->num_pcms++;
if (!spec->dig_out && !spec->dig_in)
return 0;
info++;
info->name = "CA0132 Digital";
info = snd_hda_codec_pcm_new(codec, "CA0132 Digital");
if (!info)
return -ENOMEM;
info->pcm_type = HDA_PCM_TYPE_SPDIF;
if (spec->dig_out) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
@ -4081,7 +4079,6 @@ static int ca0132_build_pcms(struct hda_codec *codec)
ca0132_pcm_digital_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
}
codec->num_pcms++;
return 0;
}
@ -4352,7 +4349,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec)
const struct dsp_image_seg *dsp_os_image;
const struct firmware *fw_entry;
if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0)
if (request_firmware(&fw_entry, EFX_FILE, codec->card->dev) != 0)
return false;
dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
@ -4413,8 +4410,7 @@ static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
* state machine run.
*/
cancel_delayed_work_sync(&spec->unsol_hp_work);
queue_delayed_work(codec->bus->workq, &spec->unsol_hp_work,
msecs_to_jiffies(500));
schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500));
cb->tbl->block_report = 1;
}
@ -4702,20 +4698,8 @@ MODULE_ALIAS("snd-hda-codec-id:11020011");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative Sound Core3D codec");
static struct hda_codec_preset_list ca0132_list = {
static struct hda_codec_driver ca0132_driver = {
.preset = snd_hda_preset_ca0132,
.owner = THIS_MODULE,
};
static int __init patch_ca0132_init(void)
{
return snd_hda_add_codec_preset(&ca0132_list);
}
static void __exit patch_ca0132_exit(void)
{
snd_hda_delete_codec_preset(&ca0132_list);
}
module_init(patch_ca0132_init)
module_exit(patch_ca0132_exit)
module_hda_codec_driver(ca0132_driver);

View file

@ -1221,20 +1221,8 @@ MODULE_ALIAS("snd-hda-codec-id:10134213");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
static struct hda_codec_preset_list cirrus_list = {
static struct hda_codec_driver cirrus_driver = {
.preset = snd_hda_preset_cirrus,
.owner = THIS_MODULE,
};
static int __init patch_cirrus_init(void)
{
return snd_hda_add_codec_preset(&cirrus_list);
}
static void __exit patch_cirrus_exit(void)
{
snd_hda_delete_codec_preset(&cirrus_list);
}
module_init(patch_cirrus_init)
module_exit(patch_cirrus_exit)
module_hda_codec_driver(cirrus_driver);

View file

@ -137,20 +137,8 @@ MODULE_ALIAS("snd-hda-codec-id:434d4980");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("C-Media HD-audio codec");
static struct hda_codec_preset_list cmedia_list = {
static struct hda_codec_driver cmedia_driver = {
.preset = snd_hda_preset_cmedia,
.owner = THIS_MODULE,
};
static int __init patch_cmedia_init(void)
{
return snd_hda_add_codec_preset(&cmedia_list);
}
static void __exit patch_cmedia_exit(void)
{
snd_hda_delete_codec_preset(&cmedia_list);
}
module_init(patch_cmedia_init)
module_exit(patch_cmedia_exit)
module_hda_codec_driver(cmedia_driver);

View file

@ -1018,20 +1018,8 @@ MODULE_ALIAS("snd-hda-codec-id:14f151d7");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
static struct hda_codec_preset_list conexant_list = {
static struct hda_codec_driver conexant_driver = {
.preset = snd_hda_preset_conexant,
.owner = THIS_MODULE,
};
static int __init patch_conexant_init(void)
{
return snd_hda_add_codec_preset(&conexant_list);
}
static void __exit patch_conexant_exit(void)
{
snd_hda_delete_codec_preset(&conexant_list);
}
module_init(patch_conexant_init)
module_exit(patch_conexant_exit)
module_hda_codec_driver(conexant_driver);

View file

@ -86,7 +86,6 @@ struct hdmi_spec_per_pin {
bool non_pcm;
bool chmap_set; /* channel-map override by ALSA API? */
unsigned char chmap[8]; /* ALSA API channel-map */
char pcm_name[8]; /* filled in build_pcm callbacks */
#ifdef CONFIG_PROC_FS
struct snd_info_entry *proc_entry;
#endif
@ -132,7 +131,7 @@ struct hdmi_spec {
int num_pins;
struct snd_array pins; /* struct hdmi_spec_per_pin */
struct snd_array pcm_rec; /* struct hda_pcm */
struct hda_pcm *pcm_rec[16];
unsigned int channels_max; /* max over all cvts */
struct hdmi_eld temp_eld;
@ -355,8 +354,7 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
#define get_cvt(spec, idx) \
((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx))
#define get_pcm_rec(spec, idx) \
((struct hda_pcm *)snd_array_elem(&spec->pcm_rec, idx))
#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx])
static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
{
@ -579,7 +577,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
int err;
snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
err = snd_card_proc_new(codec->bus->card, name, &entry);
err = snd_card_proc_new(codec->card, name, &entry);
if (err < 0)
return err;
@ -594,7 +592,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
{
if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) {
snd_device_free(per_pin->codec->bus->card, per_pin->proc_entry);
snd_device_free(per_pin->codec->card, per_pin->proc_entry);
per_pin->proc_entry = NULL;
}
}
@ -1578,9 +1576,8 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
update_eld = true;
}
else if (repoll) {
queue_delayed_work(codec->bus->workq,
&per_pin->work,
msecs_to_jiffies(300));
schedule_delayed_work(&per_pin->work,
msecs_to_jiffies(300));
goto unlock;
}
}
@ -1624,7 +1621,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
}
if (eld_changed)
snd_ctl_notify(codec->bus->card,
snd_ctl_notify(codec->card,
SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
&per_pin->eld_ctl->id);
unlock:
@ -2056,11 +2053,10 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
struct hdmi_spec_per_pin *per_pin;
per_pin = get_pin(spec, pin_idx);
sprintf(per_pin->pcm_name, "HDMI %d", pin_idx);
info = snd_array_new(&spec->pcm_rec);
info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
if (!info)
return -ENOMEM;
info->name = per_pin->pcm_name;
spec->pcm_rec[pin_idx] = info;
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->own_chmap = true;
@ -2070,9 +2066,6 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
/* other pstr fields are set in open */
}
codec->num_pcms = spec->num_pins;
codec->pcm_info = spec->pcm_rec.list;
return 0;
}
@ -2125,13 +2118,15 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
/* add channel maps */
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hda_pcm *pcm;
struct snd_pcm_chmap *chmap;
struct snd_kcontrol *kctl;
int i;
if (!codec->pcm_info[pin_idx].pcm)
pcm = spec->pcm_rec[pin_idx];
if (!pcm || !pcm->pcm)
break;
err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm,
err = snd_pcm_add_chmap_ctls(pcm->pcm,
SNDRV_PCM_STREAM_PLAYBACK,
NULL, 0, pin_idx, &chmap);
if (err < 0)
@ -2186,14 +2181,12 @@ static void hdmi_array_init(struct hdmi_spec *spec, int nums)
{
snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums);
snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums);
snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums);
}
static void hdmi_array_free(struct hdmi_spec *spec)
{
snd_array_free(&spec->pins);
snd_array_free(&spec->cvts);
snd_array_free(&spec->pcm_rec);
}
static void generic_hdmi_free(struct hda_codec *codec)
@ -2204,11 +2197,10 @@ static void generic_hdmi_free(struct hda_codec *codec)
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
cancel_delayed_work(&per_pin->work);
cancel_delayed_work_sync(&per_pin->work);
eld_proc_free(per_pin);
}
flush_workqueue(codec->bus->workq);
hdmi_array_free(spec);
kfree(spec);
}
@ -2381,11 +2373,10 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
chans = get_wcaps(codec, per_cvt->cvt_nid);
chans = get_wcaps_channels(chans);
info = snd_array_new(&spec->pcm_rec);
info = snd_hda_codec_pcm_new(codec, "HDMI 0");
if (!info)
return -ENOMEM;
info->name = get_pin(spec, 0)->pcm_name;
sprintf(info->name, "HDMI 0");
spec->pcm_rec[0] = info;
info->pcm_type = HDA_PCM_TYPE_HDMI;
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
*pstr = spec->pcm_playback;
@ -2393,9 +2384,6 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
if (pstr->channels_max <= 2 && chans && chans <= 16)
pstr->channels_max = chans;
codec->num_pcms = 1;
codec->pcm_info = info;
return 0;
}
@ -3300,15 +3288,6 @@ static int patch_via_hdmi(struct hda_codec *codec)
return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID);
}
/*
* called from hda_codec.c for generic HDMI support
*/
int snd_hda_parse_hdmi_codec(struct hda_codec *codec)
{
return patch_generic_hdmi(codec);
}
EXPORT_SYMBOL_GPL(snd_hda_parse_hdmi_codec);
/*
* patch entries
*/
@ -3373,6 +3352,8 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi },
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
/* special ID for generic HDMI */
{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi },
{} /* terminator */
};
@ -3442,20 +3423,8 @@ MODULE_ALIAS("snd-hda-codec-intelhdmi");
MODULE_ALIAS("snd-hda-codec-nvhdmi");
MODULE_ALIAS("snd-hda-codec-atihdmi");
static struct hda_codec_preset_list intel_list = {
static struct hda_codec_driver hdmi_driver = {
.preset = snd_hda_preset_hdmi,
.owner = THIS_MODULE,
};
static int __init patch_hdmi_init(void)
{
return snd_hda_add_codec_preset(&intel_list);
}
static void __exit patch_hdmi_exit(void)
{
snd_hda_delete_codec_preset(&intel_list);
}
module_init(patch_hdmi_init)
module_exit(patch_hdmi_exit)
module_hda_codec_driver(hdmi_driver);

View file

@ -2602,53 +2602,12 @@ static int patch_alc268(struct hda_codec *codec)
* ALC269
*/
static int playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
hinfo);
}
static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
stream_tag, format, substream);
}
static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}
static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_44100, /* fixed rate */
/* NID is set in alc_build_pcms */
.ops = {
.open = playback_pcm_open,
.prepare = playback_pcm_prepare,
.cleanup = playback_pcm_cleanup
},
};
static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_44100, /* fixed rate */
/* NID is set in alc_build_pcms */
};
/* different alc269-variants */
@ -5850,7 +5809,7 @@ static void alc_fixup_bass_chmap(struct hda_codec *codec,
{
if (action == HDA_FIXUP_ACT_BUILD) {
struct alc_spec *spec = codec->spec;
spec->gen.pcm_rec[0].stream[0].chmap = asus_pcm_2_1_chmaps;
spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps;
}
}
@ -6521,20 +6480,8 @@ MODULE_ALIAS("snd-hda-codec-id:10ec*");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Realtek HD-audio codec");
static struct hda_codec_preset_list realtek_list = {
static struct hda_codec_driver realtek_driver = {
.preset = snd_hda_preset_realtek,
.owner = THIS_MODULE,
};
static int __init patch_realtek_init(void)
{
return snd_hda_add_codec_preset(&realtek_list);
}
static void __exit patch_realtek_exit(void)
{
snd_hda_delete_codec_preset(&realtek_list);
}
module_init(patch_realtek_init)
module_exit(patch_realtek_exit)
module_hda_codec_driver(realtek_driver);

View file

@ -83,7 +83,6 @@
struct si3054_spec {
unsigned international;
struct hda_pcm pcm;
};
@ -199,11 +198,11 @@ static const struct hda_pcm_stream si3054_pcm = {
static int si3054_build_pcms(struct hda_codec *codec)
{
struct si3054_spec *spec = codec->spec;
struct hda_pcm *info = &spec->pcm;
codec->num_pcms = 1;
codec->pcm_info = info;
info->name = "Si3054 Modem";
struct hda_pcm *info;
info = snd_hda_codec_pcm_new(codec, "Si3054 Modem");
if (!info)
return -ENOMEM;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg;
@ -319,20 +318,8 @@ MODULE_ALIAS("snd-hda-codec-id:18540018");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
static struct hda_codec_preset_list si3054_list = {
static struct hda_codec_driver si3054_driver = {
.preset = snd_hda_preset_si3054,
.owner = THIS_MODULE,
};
static int __init patch_si3054_init(void)
{
return snd_hda_add_codec_preset(&si3054_list);
}
static void __exit patch_si3054_exit(void)
{
snd_hda_delete_codec_preset(&si3054_list);
}
module_init(patch_si3054_init)
module_exit(patch_si3054_exit)
module_hda_codec_driver(si3054_driver);

View file

@ -2132,8 +2132,10 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
#ifdef CONFIG_PM
/* resetting controller clears GPIO, so we need to keep on */
codec->bus->power_keep_link_on = 1;
codec->d3_stop_clk = 0;
#endif
}
}
@ -4223,6 +4225,12 @@ static int stac_parse_auto_config(struct hda_codec *codec)
if (err < 0)
return err;
if (spec->vref_mute_led_nid) {
err = snd_hda_gen_fix_pin_power(codec, spec->vref_mute_led_nid);
if (err < 0)
return err;
}
/* setup analog beep controls */
if (spec->anabeep_nid > 0) {
err = stac_auto_create_beep_ctls(codec,
@ -4392,6 +4400,7 @@ static const struct hda_codec_ops stac_patch_ops = {
#ifdef CONFIG_PM
.suspend = stac_suspend,
#endif
.stream_pm = snd_hda_gen_stream_pm,
.reboot_notify = stac_shutup,
};
@ -4485,6 +4494,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
return err;
spec = codec->spec;
codec->power_save_node = 1;
spec->linear_tone_beep = 0;
spec->gen.mixer_nid = 0x1d;
spec->have_spdif_mux = 1;
@ -4590,6 +4600,7 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
codec->epss = 0; /* longer delay needed for D3 */
spec = codec->spec;
codec->power_save_node = 1;
spec->linear_tone_beep = 0;
spec->gen.own_eapd_ctl = 1;
spec->gen.power_down_unused = 1;
@ -4639,6 +4650,7 @@ static int patch_stac92hd95(struct hda_codec *codec)
codec->epss = 0; /* longer delay needed for D3 */
spec = codec->spec;
codec->power_save_node = 1;
spec->linear_tone_beep = 0;
spec->gen.own_eapd_ctl = 1;
spec->gen.power_down_unused = 1;
@ -4680,6 +4692,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
return err;
spec = codec->spec;
codec->power_save_node = 1;
spec->linear_tone_beep = 0;
spec->gen.own_eapd_ctl = 1;
spec->gen.power_down_unused = 1;
@ -5091,20 +5104,8 @@ MODULE_ALIAS("snd-hda-codec-id:111d*");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
static struct hda_codec_preset_list sigmatel_list = {
static struct hda_codec_driver sigmatel_driver = {
.preset = snd_hda_preset_sigmatel,
.owner = THIS_MODULE,
};
static int __init patch_sigmatel_init(void)
{
return snd_hda_add_codec_preset(&sigmatel_list);
}
static void __exit patch_sigmatel_exit(void)
{
snd_hda_delete_codec_preset(&sigmatel_list);
}
module_init(patch_sigmatel_init)
module_exit(patch_sigmatel_exit)
module_hda_codec_driver(sigmatel_driver);

View file

@ -99,7 +99,6 @@ struct via_spec {
/* HP mode source */
unsigned int dmic_enabled;
unsigned int no_pin_power_ctl;
enum VIA_HDA_CODEC codec_type;
/* analog low-power control */
@ -108,9 +107,6 @@ struct via_spec {
/* work to check hp jack state */
int hp_work_active;
int vt1708_jack_detect;
void (*set_widgets_power_state)(struct hda_codec *codec);
unsigned int dac_stream_tag[4];
};
static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
@ -133,11 +129,12 @@ static struct via_spec *via_new_spec(struct hda_codec *codec)
/* VT1708BCE & VT1708S are almost same */
if (spec->codec_type == VT1708BCE)
spec->codec_type = VT1708S;
spec->no_pin_power_ctl = 1;
spec->gen.indep_hp = 1;
spec->gen.keep_eapd_on = 1;
spec->gen.pcm_playback_hook = via_playback_pcm_hook;
spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
codec->power_save_node = 1;
spec->gen.power_down_unused = 1;
return spec;
}
@ -222,98 +219,13 @@ static void vt1708_update_hp_work(struct hda_codec *codec)
if (!spec->hp_work_active) {
codec->jackpoll_interval = msecs_to_jiffies(100);
snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
queue_delayed_work(codec->bus->workq,
&codec->jackpoll_work, 0);
schedule_delayed_work(&codec->jackpoll_work, 0);
spec->hp_work_active = true;
}
} else if (!hp_detect_with_aa(codec))
vt1708_stop_hp_work(codec);
}
static void set_widgets_power_state(struct hda_codec *codec)
{
#if 0 /* FIXME: the assumed connections don't match always with the
* actual routes by the generic parser, so better to disable
* the control for safety.
*/
struct via_spec *spec = codec->spec;
if (spec->set_widgets_power_state)
spec->set_widgets_power_state(codec);
#endif
}
static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned int parm)
{
if (snd_hda_check_power_state(codec, nid, parm))
return;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
}
static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned int parm, unsigned int index)
{
struct via_spec *spec = codec->spec;
unsigned int format;
if (snd_hda_check_power_state(codec, nid, parm))
return;
format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
if (format && (spec->dac_stream_tag[index] != format))
spec->dac_stream_tag[index] = format;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
if (parm == AC_PWRST_D0) {
format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
if (!format && (spec->dac_stream_tag[index] != format))
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_CHANNEL_STREAMID,
spec->dac_stream_tag[index]);
}
}
static bool smart51_enabled(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
return spec->gen.ext_channel_count > 2;
}
static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
{
struct via_spec *spec = codec->spec;
int i;
for (i = 0; i < spec->gen.multi_ios; i++)
if (spec->gen.multi_io[i].pin == pin)
return true;
return false;
}
static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned int *affected_parm)
{
unsigned parm;
unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
>> AC_DEFCFG_MISC_SHIFT
& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
struct via_spec *spec = codec->spec;
unsigned present = 0;
no_presence |= spec->no_pin_power_ctl;
if (!no_presence)
present = snd_hda_jack_detect(codec, nid);
if ((smart51_enabled(codec) && is_smart51_pins(codec, nid))
|| ((no_presence || present)
&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
*affected_parm = AC_PWRST_D0; /* if it's connected */
parm = AC_PWRST_D0;
} else
parm = AC_PWRST_D3;
update_power_state(codec, nid, parm);
}
static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@ -324,8 +236,7 @@ static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
ucontrol->value.enumerated.item[0] = codec->power_save_node;
return 0;
}
@ -334,12 +245,12 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
unsigned int val = !ucontrol->value.enumerated.item[0];
bool val = !!ucontrol->value.enumerated.item[0];
if (val == spec->no_pin_power_ctl)
if (val == codec->power_save_node)
return 0;
spec->no_pin_power_ctl = val;
set_widgets_power_state(codec);
codec->power_save_node = val;
spec->gen.power_down_unused = val;
analog_low_current_mode(codec);
return 1;
}
@ -384,7 +295,7 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force)
bool enable;
unsigned int verb, parm;
if (spec->no_pin_power_ctl)
if (!codec->power_save_node)
enable = false;
else
enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
@ -441,8 +352,7 @@ static int via_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
if (spec->set_widgets_power_state)
spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
for (i = 0; i < spec->num_mixers; i++) {
err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
@ -486,7 +396,6 @@ static int via_suspend(struct hda_codec *codec)
static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
struct via_spec *spec = codec->spec;
set_widgets_power_state(codec);
analog_low_current_mode(codec);
vt1708_update_hp_work(codec);
return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
@ -574,34 +483,6 @@ static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
{} /* terminator */
};
static void via_jack_powerstate_event(struct hda_codec *codec,
struct hda_jack_callback *tbl)
{
set_widgets_power_state(codec);
}
static void via_set_jack_unsol_events(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->gen.autocfg;
hda_nid_t pin;
int i;
for (i = 0; i < cfg->line_outs; i++) {
pin = cfg->line_out_pins[i];
if (pin && is_jack_detectable(codec, pin))
snd_hda_jack_detect_enable_callback(codec, pin,
via_jack_powerstate_event);
}
for (i = 0; i < cfg->num_inputs; i++) {
pin = cfg->line_out_pins[i];
if (pin && is_jack_detectable(codec, pin))
snd_hda_jack_detect_enable_callback(codec, pin,
via_jack_powerstate_event);
}
}
static const struct badness_table via_main_out_badness = {
.no_primary_dac = 0x10000,
.no_dac = 0x4000,
@ -635,7 +516,9 @@ static int via_parse_auto_config(struct hda_codec *codec)
if (err < 0)
return err;
via_set_jack_unsol_events(codec);
/* disable widget PM at start for compatibility */
codec->power_save_node = 0;
spec->gen.power_down_unused = 0;
return 0;
}
@ -648,7 +531,6 @@ static int via_init(struct hda_codec *codec)
snd_hda_sequence_write(codec, spec->init_verbs[i]);
/* init power states */
set_widgets_power_state(codec);
__analog_low_current_mode(codec, true);
snd_hda_gen_init(codec);
@ -683,8 +565,10 @@ static int vt1708_build_pcms(struct hda_codec *codec)
* 24bit samples are used. Until any workaround is found,
* disable the 24bit format, so far.
*/
for (i = 0; i < codec->num_pcms; i++) {
struct hda_pcm *info = &spec->gen.pcm_rec[i];
for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
struct hda_pcm *info = spec->gen.pcm_rec[i];
if (!info)
continue;
if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
info->pcm_type != HDA_PCM_TYPE_AUDIO)
continue;
@ -766,78 +650,6 @@ static int patch_vt1709(struct hda_codec *codec)
return 0;
}
static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int imux_is_smixer;
unsigned int parm;
int is_8ch = 0;
if ((spec->codec_type != VT1708B_4CH) &&
(codec->vendor_id != 0x11064397))
is_8ch = 1;
/* SW0 (17h) = stereo mixer */
imux_is_smixer =
(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
== ((spec->codec_type == VT1708S) ? 5 : 0));
/* inputs */
/* PW 1/2/5 (1ah/1bh/1eh) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x1a, &parm);
set_pin_power_state(codec, 0x1b, &parm);
set_pin_power_state(codec, 0x1e, &parm);
if (imux_is_smixer)
parm = AC_PWRST_D0;
/* SW0 (17h), AIW 0/1 (13h/14h) */
update_power_state(codec, 0x17, parm);
update_power_state(codec, 0x13, parm);
update_power_state(codec, 0x14, parm);
/* outputs */
/* PW0 (19h), SW1 (18h), AOW1 (11h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x19, &parm);
if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1b, &parm);
update_power_state(codec, 0x18, parm);
update_power_state(codec, 0x11, parm);
/* PW6 (22h), SW2 (26h), AOW2 (24h) */
if (is_8ch) {
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x22, &parm);
if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1a, &parm);
update_power_state(codec, 0x26, parm);
update_power_state(codec, 0x24, parm);
} else if (codec->vendor_id == 0x11064397) {
/* PW7(23h), SW2(27h), AOW2(25h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x23, &parm);
if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1a, &parm);
update_power_state(codec, 0x27, parm);
update_power_state(codec, 0x25, parm);
}
/* PW 3/4/7 (1ch/1dh/23h) */
parm = AC_PWRST_D3;
/* force to D0 for internal Speaker */
set_pin_power_state(codec, 0x1c, &parm);
set_pin_power_state(codec, 0x1d, &parm);
if (is_8ch)
set_pin_power_state(codec, 0x23, &parm);
/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
update_power_state(codec, 0x10, parm);
if (is_8ch) {
update_power_state(codec, 0x25, parm);
update_power_state(codec, 0x27, parm);
} else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
update_power_state(codec, 0x25, parm);
}
static int patch_vt1708S(struct hda_codec *codec);
static int patch_vt1708B(struct hda_codec *codec)
{
@ -862,9 +674,6 @@ static int patch_vt1708B(struct hda_codec *codec)
}
codec->patch_ops = via_patch_ops;
spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
return 0;
}
@ -907,16 +716,16 @@ static int patch_vt1708S(struct hda_codec *codec)
if (get_codec_type(codec) == VT1708BCE) {
kfree(codec->chip_name);
codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
snprintf(codec->bus->card->mixername,
sizeof(codec->bus->card->mixername),
snprintf(codec->card->mixername,
sizeof(codec->card->mixername),
"%s %s", codec->vendor_name, codec->chip_name);
}
/* correct names for VT1705 */
if (codec->vendor_id == 0x11064397) {
kfree(codec->chip_name);
codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
snprintf(codec->bus->card->mixername,
sizeof(codec->bus->card->mixername),
snprintf(codec->card->mixername,
sizeof(codec->card->mixername),
"%s %s", codec->vendor_name, codec->chip_name);
}
@ -930,8 +739,6 @@ static int patch_vt1708S(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
codec->patch_ops = via_patch_ops;
spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
return 0;
}
@ -945,36 +752,6 @@ static const struct hda_verb vt1702_init_verbs[] = {
{ }
};
static void set_widgets_power_state_vt1702(struct hda_codec *codec)
{
int imux_is_smixer =
snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
unsigned int parm;
/* inputs */
/* PW 1/2/5 (14h/15h/18h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x14, &parm);
set_pin_power_state(codec, 0x15, &parm);
set_pin_power_state(codec, 0x18, &parm);
if (imux_is_smixer)
parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
update_power_state(codec, 0x13, parm);
update_power_state(codec, 0x12, parm);
update_power_state(codec, 0x1f, parm);
update_power_state(codec, 0x20, parm);
/* outputs */
/* PW 3/4 (16h/17h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x17, &parm);
set_pin_power_state(codec, 0x16, &parm);
/* MW0 (1ah), AOW 0/1 (10h/1dh) */
update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
update_power_state(codec, 0x10, parm);
update_power_state(codec, 0x1d, parm);
}
static int patch_vt1702(struct hda_codec *codec)
{
struct via_spec *spec;
@ -1004,8 +781,6 @@ static int patch_vt1702(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
codec->patch_ops = via_patch_ops;
spec->set_widgets_power_state = set_widgets_power_state_vt1702;
return 0;
}
@ -1020,71 +795,6 @@ static const struct hda_verb vt1718S_init_verbs[] = {
{ }
};
static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int imux_is_smixer;
unsigned int parm, parm2;
/* MUX6 (1eh) = stereo mixer */
imux_is_smixer =
snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
/* inputs */
/* PW 5/6/7 (29h/2ah/2bh) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x29, &parm);
set_pin_power_state(codec, 0x2a, &parm);
set_pin_power_state(codec, 0x2b, &parm);
if (imux_is_smixer)
parm = AC_PWRST_D0;
/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
update_power_state(codec, 0x1e, parm);
update_power_state(codec, 0x1f, parm);
update_power_state(codec, 0x10, parm);
update_power_state(codec, 0x11, parm);
/* outputs */
/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x27, &parm);
update_power_state(codec, 0x1a, parm);
parm2 = parm; /* for pin 0x0b */
/* PW2 (26h), AOW2 (ah) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x26, &parm);
if (smart51_enabled(codec))
set_pin_power_state(codec, 0x2b, &parm);
update_power_state(codec, 0xa, parm);
/* PW0 (24h), AOW0 (8h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x24, &parm);
if (!spec->gen.indep_hp_enabled) /* check for redirected HP */
set_pin_power_state(codec, 0x28, &parm);
update_power_state(codec, 0x8, parm);
if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
parm = parm2;
update_power_state(codec, 0xb, parm);
/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
/* PW1 (25h), AOW1 (9h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x25, &parm);
if (smart51_enabled(codec))
set_pin_power_state(codec, 0x2a, &parm);
update_power_state(codec, 0x9, parm);
if (spec->gen.indep_hp_enabled) {
/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x28, &parm);
update_power_state(codec, 0x1b, parm);
update_power_state(codec, 0x34, parm);
update_power_state(codec, 0xc, parm);
}
}
/* Add a connection to the primary DAC from AA-mixer for some codecs
* This isn't listed from the raw info, but the chip has a secret connection.
*/
@ -1145,9 +855,6 @@ static int patch_vt1718S(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
codec->patch_ops = via_patch_ops;
spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
return 0;
}
@ -1187,7 +894,6 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
snd_hda_codec_write(codec, 0x26, 0,
AC_VERB_SET_CONNECT_SEL, index);
spec->dmic_enabled = index;
set_widgets_power_state(codec);
return 1;
}
@ -1222,95 +928,6 @@ static const struct hda_verb vt1716S_init_verbs[] = {
{ }
};
static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int imux_is_smixer;
unsigned int parm;
unsigned int mono_out, present;
/* SW0 (17h) = stereo mixer */
imux_is_smixer =
(snd_hda_codec_read(codec, 0x17, 0,
AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
/* inputs */
/* PW 1/2/5 (1ah/1bh/1eh) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x1a, &parm);
set_pin_power_state(codec, 0x1b, &parm);
set_pin_power_state(codec, 0x1e, &parm);
if (imux_is_smixer)
parm = AC_PWRST_D0;
/* SW0 (17h), AIW0(13h) */
update_power_state(codec, 0x17, parm);
update_power_state(codec, 0x13, parm);
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x1e, &parm);
/* PW11 (22h) */
if (spec->dmic_enabled)
set_pin_power_state(codec, 0x22, &parm);
else
update_power_state(codec, 0x22, AC_PWRST_D3);
/* SW2(26h), AIW1(14h) */
update_power_state(codec, 0x26, parm);
update_power_state(codec, 0x14, parm);
/* outputs */
/* PW0 (19h), SW1 (18h), AOW1 (11h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x19, &parm);
/* Smart 5.1 PW2(1bh) */
if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1b, &parm);
update_power_state(codec, 0x18, parm);
update_power_state(codec, 0x11, parm);
/* PW7 (23h), SW3 (27h), AOW3 (25h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x23, &parm);
/* Smart 5.1 PW1(1ah) */
if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1a, &parm);
update_power_state(codec, 0x27, parm);
/* Smart 5.1 PW5(1eh) */
if (smart51_enabled(codec))
set_pin_power_state(codec, 0x1e, &parm);
update_power_state(codec, 0x25, parm);
/* Mono out */
/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
present = snd_hda_jack_detect(codec, 0x1c);
if (present)
mono_out = 0;
else {
present = snd_hda_jack_detect(codec, 0x1d);
if (!spec->gen.indep_hp_enabled && present)
mono_out = 0;
else
mono_out = 1;
}
parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
update_power_state(codec, 0x28, parm);
update_power_state(codec, 0x29, parm);
update_power_state(codec, 0x2a, parm);
/* PW 3/4 (1ch/1dh) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x1c, &parm);
set_pin_power_state(codec, 0x1d, &parm);
/* HP Independent Mode, power on AOW3 */
if (spec->gen.indep_hp_enabled)
update_power_state(codec, 0x25, parm);
/* force to D0 for internal Speaker */
/* MW0 (16h), AOW0 (10h) */
update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
}
static int patch_vt1716S(struct hda_codec *codec)
{
struct via_spec *spec;
@ -1338,8 +955,6 @@ static int patch_vt1716S(struct hda_codec *codec)
spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
codec->patch_ops = via_patch_ops;
spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
return 0;
}
@ -1365,98 +980,6 @@ static const struct hda_verb vt1802_init_verbs[] = {
{ }
};
static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int imux_is_smixer;
unsigned int parm;
unsigned int present;
/* MUX9 (1eh) = stereo mixer */
imux_is_smixer =
snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
/* inputs */
/* PW 5/6/7 (29h/2ah/2bh) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x29, &parm);
set_pin_power_state(codec, 0x2a, &parm);
set_pin_power_state(codec, 0x2b, &parm);
parm = AC_PWRST_D0;
/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
update_power_state(codec, 0x1e, parm);
update_power_state(codec, 0x1f, parm);
update_power_state(codec, 0x10, parm);
update_power_state(codec, 0x11, parm);
/* outputs */
/* AOW0 (8h)*/
update_power_state(codec, 0x8, parm);
if (spec->codec_type == VT1802) {
/* PW4 (28h), MW4 (18h), MUX4(38h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x28, &parm);
update_power_state(codec, 0x18, parm);
update_power_state(codec, 0x38, parm);
} else {
/* PW4 (26h), MW4 (1ch), MUX4(37h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x26, &parm);
update_power_state(codec, 0x1c, parm);
update_power_state(codec, 0x37, parm);
}
if (spec->codec_type == VT1802) {
/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x25, &parm);
update_power_state(codec, 0x15, parm);
update_power_state(codec, 0x35, parm);
} else {
/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x25, &parm);
update_power_state(codec, 0x19, parm);
update_power_state(codec, 0x35, parm);
}
if (spec->gen.indep_hp_enabled)
update_power_state(codec, 0x9, AC_PWRST_D0);
/* Class-D */
/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
present = snd_hda_jack_detect(codec, 0x25);
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x24, &parm);
parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
if (spec->codec_type == VT1802)
update_power_state(codec, 0x14, parm);
else
update_power_state(codec, 0x18, parm);
update_power_state(codec, 0x34, parm);
/* Mono Out */
present = snd_hda_jack_detect(codec, 0x26);
parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
if (spec->codec_type == VT1802) {
/* PW15 (33h), MW8(1ch), MUX8(3ch) */
update_power_state(codec, 0x33, parm);
update_power_state(codec, 0x1c, parm);
update_power_state(codec, 0x3c, parm);
} else {
/* PW15 (31h), MW8(17h), MUX8(3bh) */
update_power_state(codec, 0x31, parm);
update_power_state(codec, 0x17, parm);
update_power_state(codec, 0x3b, parm);
}
/* MW9 (21h) */
if (imux_is_smixer || !is_aa_path_mute(codec))
update_power_state(codec, 0x21, AC_PWRST_D0);
else
update_power_state(codec, 0x21, AC_PWRST_D3);
}
/*
* pin fix-up
*/
@ -1540,8 +1063,6 @@ static int patch_vt2002P(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
codec->patch_ops = via_patch_ops;
spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
return 0;
}
@ -1555,81 +1076,6 @@ static const struct hda_verb vt1812_init_verbs[] = {
{ }
};
static void set_widgets_power_state_vt1812(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
unsigned int parm;
unsigned int present;
/* inputs */
/* PW 5/6/7 (29h/2ah/2bh) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x29, &parm);
set_pin_power_state(codec, 0x2a, &parm);
set_pin_power_state(codec, 0x2b, &parm);
parm = AC_PWRST_D0;
/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
update_power_state(codec, 0x1e, parm);
update_power_state(codec, 0x1f, parm);
update_power_state(codec, 0x10, parm);
update_power_state(codec, 0x11, parm);
/* outputs */
/* AOW0 (8h)*/
update_power_state(codec, 0x8, AC_PWRST_D0);
/* PW4 (28h), MW4 (18h), MUX4(38h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x28, &parm);
update_power_state(codec, 0x18, parm);
update_power_state(codec, 0x38, parm);
/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x25, &parm);
update_power_state(codec, 0x15, parm);
update_power_state(codec, 0x35, parm);
if (spec->gen.indep_hp_enabled)
update_power_state(codec, 0x9, AC_PWRST_D0);
/* Internal Speaker */
/* PW0 (24h), MW0(14h), MUX0(34h) */
present = snd_hda_jack_detect(codec, 0x25);
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x24, &parm);
if (present) {
update_power_state(codec, 0x14, AC_PWRST_D3);
update_power_state(codec, 0x34, AC_PWRST_D3);
} else {
update_power_state(codec, 0x14, AC_PWRST_D0);
update_power_state(codec, 0x34, AC_PWRST_D0);
}
/* Mono Out */
/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
present = snd_hda_jack_detect(codec, 0x28);
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x31, &parm);
if (present) {
update_power_state(codec, 0x1c, AC_PWRST_D3);
update_power_state(codec, 0x3c, AC_PWRST_D3);
update_power_state(codec, 0x3e, AC_PWRST_D3);
} else {
update_power_state(codec, 0x1c, AC_PWRST_D0);
update_power_state(codec, 0x3c, AC_PWRST_D0);
update_power_state(codec, 0x3e, AC_PWRST_D0);
}
/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x33, &parm);
update_power_state(codec, 0x1d, parm);
update_power_state(codec, 0x3d, parm);
}
/* patch for vt1812 */
static int patch_vt1812(struct hda_codec *codec)
{
@ -1656,8 +1102,6 @@ static int patch_vt1812(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
codec->patch_ops = via_patch_ops;
spec->set_widgets_power_state = set_widgets_power_state_vt1812;
return 0;
}
@ -1673,84 +1117,6 @@ static const struct hda_verb vt3476_init_verbs[] = {
{ }
};
static void set_widgets_power_state_vt3476(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int imux_is_smixer;
unsigned int parm, parm2;
/* MUX10 (1eh) = stereo mixer */
imux_is_smixer =
snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
/* inputs */
/* PW 5/6/7 (29h/2ah/2bh) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x29, &parm);
set_pin_power_state(codec, 0x2a, &parm);
set_pin_power_state(codec, 0x2b, &parm);
if (imux_is_smixer)
parm = AC_PWRST_D0;
/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
update_power_state(codec, 0x1e, parm);
update_power_state(codec, 0x1f, parm);
update_power_state(codec, 0x10, parm);
update_power_state(codec, 0x11, parm);
/* outputs */
/* PW3 (27h), MW3(37h), AOW3 (bh) */
if (spec->codec_type == VT1705CF) {
parm = AC_PWRST_D3;
update_power_state(codec, 0x27, parm);
update_power_state(codec, 0x37, parm);
} else {
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x27, &parm);
update_power_state(codec, 0x37, parm);
}
/* PW2 (26h), MW2(36h), AOW2 (ah) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x26, &parm);
update_power_state(codec, 0x36, parm);
if (smart51_enabled(codec)) {
/* PW7(2bh), MW7(3bh), MUX7(1Bh) */
set_pin_power_state(codec, 0x2b, &parm);
update_power_state(codec, 0x3b, parm);
update_power_state(codec, 0x1b, parm);
}
update_conv_power_state(codec, 0xa, parm, 2);
/* PW1 (25h), MW1(35h), AOW1 (9h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x25, &parm);
update_power_state(codec, 0x35, parm);
if (smart51_enabled(codec)) {
/* PW6(2ah), MW6(3ah), MUX6(1ah) */
set_pin_power_state(codec, 0x2a, &parm);
update_power_state(codec, 0x3a, parm);
update_power_state(codec, 0x1a, parm);
}
update_conv_power_state(codec, 0x9, parm, 1);
/* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x28, &parm);
update_power_state(codec, 0x38, parm);
update_power_state(codec, 0x18, parm);
if (spec->gen.indep_hp_enabled)
update_conv_power_state(codec, 0xb, parm, 3);
parm2 = parm; /* for pin 0x0b */
/* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x24, &parm);
update_power_state(codec, 0x34, parm);
if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
parm = parm2;
update_conv_power_state(codec, 0x8, parm, 0);
/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
}
static int patch_vt3476(struct hda_codec *codec)
{
struct via_spec *spec;
@ -1774,9 +1140,6 @@ static int patch_vt3476(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
codec->patch_ops = via_patch_ops;
spec->set_widgets_power_state = set_widgets_power_state_vt3476;
return 0;
}
@ -1884,23 +1247,11 @@ static const struct hda_codec_preset snd_hda_preset_via[] = {
MODULE_ALIAS("snd-hda-codec-id:1106*");
static struct hda_codec_preset_list via_list = {
static struct hda_codec_driver via_driver = {
.preset = snd_hda_preset_via,
.owner = THIS_MODULE,
};
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VIA HD-audio codec");
static int __init patch_via_init(void)
{
return snd_hda_add_codec_preset(&via_list);
}
static void __exit patch_via_exit(void)
{
snd_hda_delete_codec_preset(&via_list);
}
module_init(patch_via_init)
module_exit(patch_via_exit)
module_hda_codec_driver(via_driver);

View file

@ -29,12 +29,19 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <linux/slab.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "wtm.h"
#include "stac946x.h"
struct wtm_spec {
/* rate change needs atomic mute/unmute of all dacs*/
struct mutex mute_mutex;
};
/*
* 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
@ -68,15 +75,65 @@ static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg)
/*
* DAC mute control
*/
static void stac9460_dac_mute_all(struct snd_ice1712 *ice, unsigned char mute,
unsigned short int *change_mask)
{
unsigned char new, old;
int id, idx, change;
/*stac9460 1*/
for (id = 0; id < 7; id++) {
if (*change_mask & (0x01 << id)) {
if (id == 0)
idx = STAC946X_MASTER_VOLUME;
else
idx = STAC946X_LF_VOLUME - 1 + id;
old = stac9460_get(ice, idx);
new = (~mute << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change) {
stac9460_put(ice, idx, new);
*change_mask = *change_mask | (0x01 << id);
} else {
*change_mask = *change_mask & ~(0x01 << id);
}
}
}
/*stac9460 2*/
for (id = 0; id < 3; id++) {
if (*change_mask & (0x01 << (id + 7))) {
if (id == 0)
idx = STAC946X_MASTER_VOLUME;
else
idx = STAC946X_LF_VOLUME - 1 + id;
old = stac9460_2_get(ice, idx);
new = (~mute << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change) {
stac9460_2_put(ice, idx, new);
*change_mask = *change_mask | (0x01 << id);
} else {
*change_mask = *change_mask & ~(0x01 << id);
}
}
}
}
#define stac9460_dac_mute_info snd_ctl_boolean_mono_info
static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct wtm_spec *spec = ice->spec;
unsigned char val;
int idx, id;
mutex_lock(&spec->mute_mutex);
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
id = 0;
@ -89,6 +146,8 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
else
val = stac9460_2_get(ice, idx - 6);
ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
mutex_unlock(&spec->mute_mutex);
return 0;
}
@ -338,8 +397,14 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
/*
* MIC / LINE switch fonction
*/
static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[2] = { "Line In", "Mic" };
return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
#define stac9460_mic_sw_info snd_ctl_boolean_mono_info
static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@ -353,7 +418,7 @@ static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
else
val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
ucontrol->value.integer.value[0] = ~val>>7 & 0x1;
ucontrol->value.enumerated.item[0] = (val >> 7) & 0x1;
return 0;
}
@ -369,7 +434,7 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
else
old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
new = (~ucontrol->value.integer.value[0] << 7 & 0x80) | (old & ~0x80);
new = (ucontrol->value.enumerated.item[0] << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change) {
if (id == 0)
@ -380,17 +445,63 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
return change;
}
/*
* Handler for setting correct codec rate - called when rate change is detected
*/
static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
{
unsigned char old, new;
unsigned short int changed;
struct wtm_spec *spec = ice->spec;
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
return;
else if (rate <= 48000)
new = 0x08; /* 256x, base rate mode */
else if (rate <= 96000)
new = 0x11; /* 256x, mid rate mode */
else
new = 0x12; /* 128x, high rate mode */
old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
if (old == new)
return;
/* change detected, setting master clock, muting first */
/* due to possible conflicts with mute controls - mutexing */
mutex_lock(&spec->mute_mutex);
/* we have to remember current mute status for each DAC */
changed = 0xFFFF;
stac9460_dac_mute_all(ice, 0, &changed);
/*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
stac9460_2_put(ice, STAC946X_MASTER_CLOCKING, new);
udelay(10);
/* unmuting - only originally unmuted dacs -
* i.e. those changed when muting */
stac9460_dac_mute_all(ice, 1, &changed);
mutex_unlock(&spec->mute_mutex);
}
/*Limits value in dB for fader*/
static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
/*
* Control tabs
*/
static struct snd_kcontrol_new stac9640_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Switch",
.info = stac9460_dac_mute_info,
.get = stac9460_dac_mute_get,
.put = stac9460_dac_mute_put,
.private_value = 1
.private_value = 1,
.tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -402,7 +513,7 @@ static struct snd_kcontrol_new stac9640_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIC/Line switch",
.name = "MIC/Line Input Enum",
.count = 2,
.info = stac9460_mic_sw_info,
.get = stac9460_mic_sw_get,
@ -419,11 +530,15 @@ static struct snd_kcontrol_new stac9640_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "DAC Volume",
.count = 8,
.info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put,
.tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -435,12 +550,15 @@ static struct snd_kcontrol_new stac9640_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "ADC Volume",
.count = 2,
.info = stac9460_adc_vol_info,
.get = stac9460_adc_vol_get,
.put = stac9460_adc_vol_put,
.tlv = { .p = db_scale_adc }
}
};
@ -463,41 +581,53 @@ static int wtm_add_controls(struct snd_ice1712 *ice)
static int wtm_init(struct snd_ice1712 *ice)
{
static unsigned short stac_inits_prodigy[] = {
static unsigned short stac_inits_wtm[] = {
STAC946X_RESET, 0,
STAC946X_MASTER_CLOCKING, 0x11,
(unsigned short)-1
};
unsigned short *p;
struct wtm_spec *spec;
/*WTM 192M*/
ice->num_total_dacs = 8;
ice->num_total_adcs = 4;
ice->force_rdma1 = 1;
/*init mutex for dac mute conflict*/
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
ice->spec = spec;
mutex_init(&spec->mute_mutex);
/*initialize codec*/
p = stac_inits_prodigy;
p = stac_inits_wtm;
for (; *p != (unsigned short)-1; p += 2) {
stac9460_put(ice, p[0], p[1]);
stac9460_2_put(ice, p[0], p[1]);
}
ice->gpio.set_pro_rate = stac9460_set_rate_val;
return 0;
}
static unsigned char wtm_eeprom[] = {
0x47, /*SYSCONF: clock 192KHz, 4ADC, 8DAC */
0x80, /* ACLINK : I2S */
0xf8, /* I2S: vol; 96k, 24bit, 192k */
0xc1 /*SPDIF: out-en, spidf ext out*/,
0x9f, /* GPIO_DIR */
0xff, /* GPIO_DIR1 */
0x7f, /* GPIO_DIR2 */
0x9f, /* GPIO_MASK */
0xff, /* GPIO_MASK1 */
0x7f, /* GPIO_MASK2 */
0x16, /* GPIO_STATE */
0x80, /* GPIO_STATE1 */
0x00, /* GPIO_STATE2 */
[ICE_EEP2_SYSCONF] = 0x67, /*SYSCONF: clock 192KHz, mpu401,
4ADC, 8DAC */
[ICE_EEP2_ACLINK] = 0x80, /* ACLINK : I2S */
[ICE_EEP2_I2S] = 0xf8, /* I2S: vol; 96k, 24bit, 192k */
[ICE_EEP2_SPDIF] = 0xc1, /*SPDIF: out-en, spidf ext out*/
[ICE_EEP2_GPIO_DIR] = 0x9f,
[ICE_EEP2_GPIO_DIR1] = 0xff,
[ICE_EEP2_GPIO_DIR2] = 0x7f,
[ICE_EEP2_GPIO_MASK] = 0x9f,
[ICE_EEP2_GPIO_MASK1] = 0xff,
[ICE_EEP2_GPIO_MASK2] = 0x7f,
[ICE_EEP2_GPIO_STATE] = 0x16,
[ICE_EEP2_GPIO_STATE1] = 0x80,
[ICE_EEP2_GPIO_STATE2] = 0x00,
};

View file

@ -6043,23 +6043,30 @@ hdspm_hw_constraints_aes32_sample_rates = {
.mask = 0
};
static int snd_hdspm_playback_open(struct snd_pcm_substream *substream)
static int snd_hdspm_open(struct snd_pcm_substream *substream)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
spin_lock_irq(&hdspm->lock);
snd_pcm_set_sync(substream);
runtime->hw = (playback) ? snd_hdspm_playback_subinfo :
snd_hdspm_capture_subinfo;
if (playback) {
if (hdspm->capture_substream == NULL)
hdspm_stop_audio(hdspm);
runtime->hw = snd_hdspm_playback_subinfo;
hdspm->playback_pid = current->pid;
hdspm->playback_substream = substream;
} else {
if (hdspm->playback_substream == NULL)
hdspm_stop_audio(hdspm);
if (hdspm->capture_substream == NULL)
hdspm_stop_audio(hdspm);
hdspm->playback_pid = current->pid;
hdspm->playback_substream = substream;
hdspm->capture_pid = current->pid;
hdspm->capture_substream = substream;
}
spin_unlock_irq(&hdspm->lock);
@ -6094,108 +6101,42 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream)
&hdspm_hw_constraints_aes32_sample_rates);
} else {
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
snd_hdspm_hw_rule_rate_out_channels, hdspm,
(playback ?
snd_hdspm_hw_rule_rate_out_channels :
snd_hdspm_hw_rule_rate_in_channels), hdspm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
}
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
snd_hdspm_hw_rule_out_channels, hdspm,
(playback ? snd_hdspm_hw_rule_out_channels :
snd_hdspm_hw_rule_in_channels), hdspm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
snd_hdspm_hw_rule_out_channels_rate, hdspm,
(playback ? snd_hdspm_hw_rule_out_channels_rate :
snd_hdspm_hw_rule_in_channels_rate), hdspm,
SNDRV_PCM_HW_PARAM_RATE, -1);
return 0;
}
static int snd_hdspm_playback_release(struct snd_pcm_substream *substream)
static int snd_hdspm_release(struct snd_pcm_substream *substream)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
spin_lock_irq(&hdspm->lock);
hdspm->playback_pid = -1;
hdspm->playback_substream = NULL;
spin_unlock_irq(&hdspm->lock);
return 0;
}
static int snd_hdspm_capture_open(struct snd_pcm_substream *substream)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
spin_lock_irq(&hdspm->lock);
snd_pcm_set_sync(substream);
runtime->hw = snd_hdspm_capture_subinfo;
if (hdspm->playback_substream == NULL)
hdspm_stop_audio(hdspm);
hdspm->capture_pid = current->pid;
hdspm->capture_substream = substream;
spin_unlock_irq(&hdspm->lock);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
switch (hdspm->io_type) {
case AIO:
case RayDAT:
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
32, 4096);
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
16384, 16384);
break;
default:
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
64, 8192);
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIODS,
2, 2);
break;
}
if (AES32 == hdspm->io_type) {
runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&hdspm_hw_constraints_aes32_sample_rates);
if (playback) {
hdspm->playback_pid = -1;
hdspm->playback_substream = NULL;
} else {
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
snd_hdspm_hw_rule_rate_in_channels, hdspm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
hdspm->capture_pid = -1;
hdspm->capture_substream = NULL;
}
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
snd_hdspm_hw_rule_in_channels, hdspm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
snd_hdspm_hw_rule_in_channels_rate, hdspm,
SNDRV_PCM_HW_PARAM_RATE, -1);
return 0;
}
static int snd_hdspm_capture_release(struct snd_pcm_substream *substream)
{
struct hdspm *hdspm = snd_pcm_substream_chip(substream);
spin_lock_irq(&hdspm->lock);
hdspm->capture_pid = -1;
hdspm->capture_substream = NULL;
spin_unlock_irq(&hdspm->lock);
return 0;
}
@ -6413,21 +6354,9 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
return 0;
}
static struct snd_pcm_ops snd_hdspm_playback_ops = {
.open = snd_hdspm_playback_open,
.close = snd_hdspm_playback_release,
.ioctl = snd_hdspm_ioctl,
.hw_params = snd_hdspm_hw_params,
.hw_free = snd_hdspm_hw_free,
.prepare = snd_hdspm_prepare,
.trigger = snd_hdspm_trigger,
.pointer = snd_hdspm_hw_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
static struct snd_pcm_ops snd_hdspm_capture_ops = {
.open = snd_hdspm_capture_open,
.close = snd_hdspm_capture_release,
static struct snd_pcm_ops snd_hdspm_ops = {
.open = snd_hdspm_open,
.close = snd_hdspm_release,
.ioctl = snd_hdspm_ioctl,
.hw_params = snd_hdspm_hw_params,
.hw_free = snd_hdspm_hw_free,
@ -6521,9 +6450,9 @@ static int snd_hdspm_create_pcm(struct snd_card *card,
strcpy(pcm->name, hdspm->card_name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_hdspm_playback_ops);
&snd_hdspm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_hdspm_capture_ops);
&snd_hdspm_ops);
pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;

View file

@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
/*
* When the bit clock is input, limit the maximum rate according to the
* Serial Clock Ratio Considerations section from the SSC documentation:
*
* The Transmitter and the Receiver can be programmed to operate
* with the clock signals provided on either the TK or RK pins.
* This allows the SSC to support many slave-mode data transfers.
* In this case, the maximum clock speed allowed on the RK pin is:
* - Peripheral clock divided by 2 if Receiver Frame Synchro is input
* - Peripheral clock divided by 3 if Receiver Frame Synchro is output
* In addition, the maximum clock speed allowed on the TK pin is:
* - Peripheral clock divided by 6 if Transmit Frame Synchro is input
* - Peripheral clock divided by 2 if Transmit Frame Synchro is output
*
* When the bit clock is output, limit the rate according to the
* SSC divider restrictions.
*/
static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct atmel_ssc_info *ssc_p = rule->private;
struct ssc_device *ssc = ssc_p->ssc;
struct snd_interval *i = hw_param_interval(params, rule->var);
struct snd_interval t;
struct snd_ratnum r = {
.den_min = 1,
.den_max = 4095,
.den_step = 1,
};
unsigned int num = 0, den = 0;
int frame_size;
int mck_div = 2;
int ret;
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0)
return frame_size;
switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFS:
if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
&& ssc->clk_from_rk_pin)
/* Receiver Frame Synchro (i.e. capture)
* is output (format is _CFS) and the RK pin
* is used for input (format is _CBM_).
*/
mck_div = 3;
break;
case SND_SOC_DAIFMT_CBM_CFM:
if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
&& !ssc->clk_from_rk_pin)
/* Transmit Frame Synchro (i.e. playback)
* is input (format is _CFM) and the TK pin
* is used for input (format _CBM_ but not
* using the RK pin).
*/
mck_div = 6;
break;
}
switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
r.num = ssc_p->mck_rate / mck_div / frame_size;
ret = snd_interval_ratnum(i, 1, &r, &num, &den);
if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
params->rate_num = num;
params->rate_den = den;
}
break;
case SND_SOC_DAIFMT_CBM_CFS:
case SND_SOC_DAIFMT_CBM_CFM:
t.min = 8000;
t.max = ssc_p->mck_rate / mck_div / frame_size;
t.openmin = t.openmax = 0;
t.integer = 0;
ret = snd_interval_refine(i, &t);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/*-------------------------------------------------------------------------*\
* DAI functions
@ -200,6 +288,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask;
int ret;
pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
ssc_readl(ssc_p->ssc->regs, SR));
@ -207,6 +296,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
/* Enable PMC peripheral clock for this SSC */
pr_debug("atmel_ssc_dai: Starting clock\n");
clk_enable(ssc_p->ssc->clk);
ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
/* Reset the SSC to keep it at a clean status */
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
@ -219,6 +309,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
dir_mask = SSC_DIR_MASK_CAPTURE;
}
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
atmel_ssc_hw_rule_rate,
ssc_p,
SNDRV_PCM_HW_PARAM_FRAME_BITS,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (ret < 0) {
dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret);
return ret;
}
dma_params = &ssc_dma_params[dai->id][dir];
dma_params->ssc = ssc_p->ssc;
dma_params->substream = substream;
@ -783,8 +884,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
# define atmel_ssc_resume NULL
#endif /* CONFIG_PM */
#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@ -804,12 +903,16 @@ static struct snd_soc_dai_driver atmel_ssc_dai = {
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 8000,
.rate_max = 384000,
.formats = ATMEL_SSC_FORMATS,},
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 8000,
.rate_max = 384000,
.formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops,
};

View file

@ -115,6 +115,7 @@ struct atmel_ssc_info {
unsigned short rcmr_period;
struct atmel_pcm_dma_params *dma_params[2];
struct atmel_ssc_state ssc_state;
unsigned long mck_rate;
};
int atmel_ssc_set_audio(int ssc_id);

View file

@ -141,7 +141,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8770 if SPI_MASTER
select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8782
select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8804_I2C if I2C
select SND_SOC_WM8804_SPI if SPI_MASTER
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
@ -744,8 +745,19 @@ config SND_SOC_WM8782
tristate
config SND_SOC_WM8804
tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver"
depends on SND_SOC_I2C_AND_SPI
tristate
config SND_SOC_WM8804_I2C
tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver I2C"
depends on I2C
select SND_SOC_WM8804
select REGMAP_I2C
config SND_SOC_WM8804_SPI
tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver SPI"
depends on SPI_MASTER
select SND_SOC_WM8804
select REGMAP_SPI
config SND_SOC_WM8900
tristate

View file

@ -145,6 +145,8 @@ snd-soc-wm8770-objs := wm8770.o
snd-soc-wm8776-objs := wm8776.o
snd-soc-wm8782-objs := wm8782.o
snd-soc-wm8804-objs := wm8804.o
snd-soc-wm8804-i2c-objs := wm8804-i2c.o
snd-soc-wm8804-spi-objs := wm8804-spi.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
@ -323,6 +325,8 @@ obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o
obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
obj-$(CONFIG_SND_SOC_WM8804_I2C) += snd-soc-wm8804-i2c.o
obj-$(CONFIG_SND_SOC_WM8804_SPI) += snd-soc-wm8804-spi.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o

View file

@ -938,22 +938,15 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
adau1977->dvdd_reg = NULL;
}
adau1977->reset_gpio = devm_gpiod_get(dev, "reset");
if (IS_ERR(adau1977->reset_gpio)) {
ret = PTR_ERR(adau1977->reset_gpio);
if (ret != -ENOENT && ret != -ENOSYS)
return PTR_ERR(adau1977->reset_gpio);
adau1977->reset_gpio = NULL;
}
adau1977->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(adau1977->reset_gpio))
return PTR_ERR(adau1977->reset_gpio);
dev_set_drvdata(dev, adau1977);
if (adau1977->reset_gpio) {
ret = gpiod_direction_output(adau1977->reset_gpio, 0);
if (ret)
return ret;
if (adau1977->reset_gpio)
ndelay(100);
}
ret = adau1977_power_enable(adau1977);
if (ret)

View file

@ -437,20 +437,13 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
}
/* Reset the Device */
cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev,
"reset-gpios");
if (IS_ERR(cs35l32->reset_gpio)) {
ret = PTR_ERR(cs35l32->reset_gpio);
if (ret != -ENOENT && ret != -ENOSYS)
return ret;
cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(cs35l32->reset_gpio))
return PTR_ERR(cs35l32->reset_gpio);
cs35l32->reset_gpio = NULL;
} else {
ret = gpiod_direction_output(cs35l32->reset_gpio, 0);
if (ret)
return ret;
if (cs35l32->reset_gpio)
gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
}
/* initialize codec */
ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, &reg);

Some files were not shown because too many files have changed in this diff Show more