mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-25 03:50:53 +07:00
Merge branch 'for-next' into topic/hda-core
This commit is contained in:
commit
3372dbdd8c
@ -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.
|
||||
|
@ -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
|
||||
|
200
Documentation/sound/alsa/timestamping.txt
Normal file
200
Documentation/sound/alsa/timestamping.txt
Normal 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
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -23,8 +23,7 @@
|
||||
#define _UAPI__SOUND_EMU10K1_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
|
||||
#include <sound/asound.h>
|
||||
|
||||
/*
|
||||
* ---- FX8010 ----
|
||||
|
@ -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
|
||||
|
||||
|
@ -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" },
|
||||
{ }
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
105
sound/core/pcm.c
105
sound/core/pcm.c
@ -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(®ister_mutex);
|
||||
err = snd_pcm_add(pcm);
|
||||
if (err) {
|
||||
mutex_unlock(®ister_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(®ister_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(®ister_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(®ister_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(®ister_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 },
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
342
sound/pci/hda/hda_bind.c
Normal 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
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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 */
|
||||
|
@ -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 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec)
|
||||
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");
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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, ®);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user