From 55ac43f6a506e806fe3de020221955df160799dd Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 14 Feb 2011 07:36:16 -0600 Subject: [PATCH 52/60] ASoC: core: make pcm_new() pass only snd_soc_pcm_runtime * commit 39c0d679fa516c181398ea5ae4f1b77e99a2c9ba upstream Reduce number of arguments passed in pcm_new() from three to one by passing snd_soc_pcm_runtime*. This allows allows DSP DAI components to better determine buffers, channels etc. Change-Id: Ib3ba3e9d035433e1628fd4a21f8d9ec73810dab1 Signed-off-by: Liam Girdwood Signed-off-by: Misael Lopez Cruz Signed-off-by: Margarita Olaya Cabrera Integrated-by: Jingdong Lu --- include/sound/soc.h | 251 ++++------ sound/soc/omap/omap-pcm.c | 9 +- sound/soc/soc-core.c | 1243 +++++++++++++++++++++++++-------------------- 3 files changed, 781 insertions(+), 722 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 3a4bd3a..41b931a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -234,8 +234,8 @@ struct snd_soc_codec; struct snd_soc_codec_driver; struct soc_enum; struct snd_soc_jack; -struct snd_soc_jack_zone; struct snd_soc_jack_pin; +struct snd_soc_dapm_context; struct snd_soc_cache_ops; #include @@ -248,7 +248,8 @@ typedef int (*hw_write_t)(void *,const char* ,int); extern struct snd_ac97_bus_ops soc_ac97_ops; enum snd_soc_control_type { - SND_SOC_I2C = 1, + SND_SOC_CUSTOM, + SND_SOC_I2C, SND_SOC_SPI, }; @@ -257,17 +258,13 @@ enum snd_soc_compress_type { SND_SOC_LZO_COMPRESSION, SND_SOC_RBTREE_COMPRESSION }; +/* Max number of Backend DAIs */ +#define SND_SOC_MAX_BE 8 -int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, - unsigned int freq, int dir); -int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, - unsigned int freq_in, unsigned int freq_out); +/* DAI Link Host Mode Support */ +#define SND_SOC_DAI_LINK_NO_HOST 0x1 +#define SND_SOC_DAI_LINK_OPT_HOST 0x2 -int snd_soc_register_card(struct snd_soc_card *card); -int snd_soc_unregister_card(struct snd_soc_card *card); -int snd_soc_suspend(struct device *dev); -int snd_soc_resume(struct device *dev); -int snd_soc_poweroff(struct device *dev); int snd_soc_register_platform(struct device *dev, struct snd_soc_platform_driver *platform_drv); void snd_soc_unregister_platform(struct device *dev); @@ -275,12 +272,7 @@ int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_codec(struct device *dev); -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_codec_readable_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_codec_writable_register(struct snd_soc_codec *codec, - unsigned int reg); +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg); int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, int addr_bits, int data_bits, enum snd_soc_control_type control); @@ -291,12 +283,24 @@ int snd_soc_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value); int snd_soc_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value); -int snd_soc_default_volatile_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_default_readable_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_default_writable_register(struct snd_soc_codec *codec, - unsigned int reg); + +/* pcm <-> DAI connect */ +void snd_soc_free_pcms(struct snd_soc_codec *codec); +int snd_soc_new_pcms(struct snd_soc_codec *codec, int idx, const char *xid); + +/* DAI operations - for backend DAIs */ +int snd_soc_pcm_open(struct snd_pcm_substream *substream); +int snd_soc_pcm_close(struct snd_pcm_substream *substream); +int snd_soc_pcm_prepare(struct snd_pcm_substream *substream); +int snd_soc_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int snd_soc_pcm_hw_free(struct snd_pcm_substream *substream); +int snd_soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd); +snd_pcm_uframes_t snd_soc_pcm_pointer(struct snd_pcm_substream *substream); +struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, + const char *dai_link, int stream); +struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, + const char *dai_link); /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); @@ -318,9 +322,6 @@ void snd_soc_jack_notifier_register(struct snd_soc_jack *jack, struct notifier_block *nb); void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack, struct notifier_block *nb); -int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, - struct snd_soc_jack_zone *zones); -int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage); #ifdef CONFIG_GPIOLIB int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, struct snd_soc_jack_gpio *gpios); @@ -345,10 +346,11 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); *Controls */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, - void *data, char *long_name, - const char *prefix); + void *data, char *long_name); int snd_soc_add_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls); +int snd_soc_add_platform_controls(struct snd_soc_platform *platform, + const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, @@ -392,22 +394,6 @@ int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); /** - * struct snd_soc_reg_access - Describes whether a given register is - * readable, writable or volatile. - * - * @reg: the register number - * @read: whether this register is readable - * @write: whether this register is writable - * @vol: whether this register is volatile - */ -struct snd_soc_reg_access { - u16 reg; - u16 read; - u16 write; - u16 vol; -}; - -/** * struct snd_soc_jack_pin - Describes a pin to update based on jack detection * * @pin: name of the pin to update @@ -422,24 +408,6 @@ struct snd_soc_jack_pin { }; /** - * struct snd_soc_jack_zone - Describes voltage zones of jack detection - * - * @min_mv: start voltage in mv - * @max_mv: end voltage in mv - * @jack_type: type of jack that is expected for this voltage - * @debounce_time: debounce_time for jack, codec driver should wait for this - * duration before reading the adc for voltages - * @:list: list container - */ -struct snd_soc_jack_zone { - unsigned int min_mv; - unsigned int max_mv; - unsigned int jack_type; - unsigned int debounce_time; - struct list_head list; -}; - -/** * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection * * @gpio: gpio number @@ -447,10 +415,6 @@ struct snd_soc_jack_zone { * @report: value to report when jack detected * @invert: report presence in low state * @debouce_time: debouce time in ms - * @wake: enable as wake source - * @jack_status_check: callback function which overrides the detection - * to provide more complex checks (eg, reading an - * ADC). */ #ifdef CONFIG_GPIOLIB struct snd_soc_jack_gpio { @@ -459,8 +423,6 @@ struct snd_soc_jack_gpio { int report; int invert; int debounce_time; - bool wake; - struct snd_soc_jack *jack; struct delayed_work work; @@ -474,7 +436,6 @@ struct snd_soc_jack { struct list_head pins; int status; struct blocking_notifier_head notifier; - struct list_head jack_zones; }; /* SoC PCM stream information */ @@ -525,32 +486,25 @@ struct snd_soc_codec { struct list_head card_list; int num_dai; enum snd_soc_compress_type compress_type; - size_t reg_size; /* reg_cache_size * reg_word_size */ - int (*volatile_register)(struct snd_soc_codec *, unsigned int); - int (*readable_register)(struct snd_soc_codec *, unsigned int); - int (*writable_register)(struct snd_soc_codec *, unsigned int); /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ unsigned int active; - unsigned int cache_bypass:1; /* Suppress access to the cache */ + unsigned int cache_only:1; /* Suppress writes to hardware */ + unsigned int cache_sync:1; /* Cache needs to be synced to hardware */ unsigned int suspended:1; /* Codec is in suspend PM state */ unsigned int probed:1; /* Codec has been probed */ unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int sysfs_registered:1; /* codec has been sysfs registered */ unsigned int cache_init:1; /* codec cache has been initialized */ - u32 cache_only; /* Suppress writes to hardware */ - u32 cache_sync; /* Cache needs to be synced to hardware */ /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ - enum snd_soc_control_type control_type; hw_write_t hw_write; unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); - int (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t); void *reg_cache; const void *reg_def_copy; const struct snd_soc_cache_ops *cache_ops; @@ -576,42 +530,24 @@ struct snd_soc_codec_driver { pm_message_t state); int (*resume)(struct snd_soc_codec *); - /* Default control and setup, added after probe() is run */ - const struct snd_kcontrol_new *controls; - int num_controls; - const struct snd_soc_dapm_widget *dapm_widgets; - int num_dapm_widgets; - const struct snd_soc_dapm_route *dapm_routes; - int num_dapm_routes; - - /* codec wide operations */ - int (*set_sysclk)(struct snd_soc_codec *codec, - int clk_id, unsigned int freq, int dir); - int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source, - unsigned int freq_in, unsigned int freq_out); - /* codec IO */ unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); int (*display_register)(struct snd_soc_codec *, char *, size_t, unsigned int); - int (*volatile_register)(struct snd_soc_codec *, unsigned int); - int (*readable_register)(struct snd_soc_codec *, unsigned int); - int (*writable_register)(struct snd_soc_codec *, unsigned int); + int (*volatile_register)(unsigned int); + int (*readable_register)(unsigned int); short reg_cache_size; short reg_cache_step; short reg_word_size; const void *reg_cache_default; - short reg_access_size; - const struct snd_soc_reg_access *reg_access_default; enum snd_soc_compress_type compress_type; /* codec bias level */ int (*set_bias_level)(struct snd_soc_codec *, enum snd_soc_bias_level level); - - void (*seq_notifier)(struct snd_soc_dapm_context *, - enum snd_soc_dapm_type, int); + /* codec stream completion event */ + int (*stream_event)(struct snd_soc_dapm_context *dapm); }; /* SoC platform interface */ @@ -623,8 +559,7 @@ struct snd_soc_platform_driver { int (*resume)(struct snd_soc_dai *dai); /* pcm creation and destruction */ - int (*pcm_new)(struct snd_card *, struct snd_soc_dai *, - struct snd_pcm *); + int (*pcm_new)(struct snd_soc_pcm_runtime *); void (*pcm_free)(struct snd_pcm *); /* @@ -636,6 +571,13 @@ struct snd_soc_platform_driver { /* platform stream ops */ struct snd_pcm_ops *ops; + + /* platform DAPM IO TODO: refactor this */ + unsigned int (*read)(struct snd_soc_platform *, unsigned int); + int (*write)(struct snd_soc_platform *, unsigned int, unsigned int); + + /* platform stream completion event */ + int (*stream_event)(struct snd_soc_dapm_context *dapm); }; struct snd_soc_platform { @@ -650,6 +592,10 @@ struct snd_soc_platform { struct snd_soc_card *card; struct list_head list; struct list_head card_list; + int num_dai; + + /* dapm */ + struct snd_soc_dapm_context dapm; }; struct snd_soc_dai_link { @@ -661,15 +607,36 @@ struct snd_soc_dai_link { const char *cpu_dai_name; const char *codec_dai_name; + /* supported BE */ + const char **supported_be; + int num_be; + int fe_playback_channels; + int fe_capture_channels; + + /* Keep DAI active over suspend */ unsigned int ignore_suspend:1; /* Symmetry requirements */ unsigned int symmetric_rates:1; + /* No PCM created for this DAI link */ + unsigned int no_pcm:1; + /* This DAI link can change CODEC and platform at runtime*/ + unsigned int dynamic:1; + /* This DAI link has no codec side driver*/ + unsigned int no_codec:1; + /* This DAI has a Backend ID */ + unsigned int be_id; + /* This DAI can support no host IO (no pcm data is copied to from host) */ + unsigned int no_host_mode:2; /* codec/machine specific init - e.g. add machine controls */ int (*init)(struct snd_soc_pcm_runtime *rtd); + /* hw_params re-writing for BE and FE sync */ + int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); + /* machine stream operations */ struct snd_soc_ops *ops; }; @@ -702,26 +669,25 @@ struct snd_soc_aux_dev { struct snd_soc_card { const char *name; const char *long_name; - const char *driver_name; struct device *dev; struct snd_card *snd_card; struct module *owner; struct list_head list; struct mutex mutex; + struct mutex dapm_mutex; bool instantiated; - int (*probe)(struct snd_soc_card *card); - int (*late_probe)(struct snd_soc_card *card); - int (*remove)(struct snd_soc_card *card); + int (*probe)(struct platform_device *pdev); + int (*remove)(struct platform_device *pdev); /* the pre and post PM functions are used to do any PM work before and * after the codec and DAI's do any PM work. */ - int (*suspend_pre)(struct snd_soc_card *card); - int (*suspend_post)(struct snd_soc_card *card); - int (*resume_pre)(struct snd_soc_card *card); - int (*resume_post)(struct snd_soc_card *card); + int (*suspend_pre)(struct platform_device *pdev, pm_message_t state); + int (*suspend_post)(struct platform_device *pdev, pm_message_t state); + int (*resume_pre)(struct platform_device *pdev); + int (*resume_post)(struct platform_device *pdev); /* callbacks */ int (*set_bias_level)(struct snd_soc_card *, @@ -750,17 +716,6 @@ struct snd_soc_card { struct snd_soc_pcm_runtime *rtd_aux; int num_aux_rtd; - const struct snd_kcontrol_new *controls; - int num_controls; - - /* - * Card-specific routes and widgets. - */ - const struct snd_soc_dapm_widget *dapm_widgets; - int num_dapm_widgets; - const struct snd_soc_dapm_route *dapm_routes; - int num_dapm_routes; - struct work_struct deferred_resume_work; /* lists of probed devices belonging to this card */ @@ -772,16 +727,11 @@ struct snd_soc_card { struct list_head paths; struct list_head dapm_list; - /* Generic DAPM context for the card */ - struct snd_soc_dapm_context dapm; - #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; struct dentry *debugfs_pop_time; #endif u32 pop_time; - - void *drvdata; }; /* SoC machine DAI configuration, glues a codec and cpu DAI together */ @@ -789,10 +739,18 @@ struct snd_soc_pcm_runtime { struct device dev; struct snd_soc_card *card; struct snd_soc_dai_link *dai_link; + struct mutex pcm_mutex; + struct snd_pcm_ops ops; unsigned int complete:1; unsigned int dev_registered:1; + /* BE runtime data */ + unsigned int fe_clients; + unsigned int num_be[2]; + unsigned int be_active; + struct snd_soc_pcm_runtime *be_rtd[SND_SOC_MAX_BE][2]; + /* Symmetry data - only valid if symmetry is being enforced */ unsigned int rate; long pmdown_time; @@ -803,6 +761,7 @@ struct snd_soc_pcm_runtime { struct snd_soc_platform *platform; struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; + int current_fe; struct delayed_work delayed_work; }; @@ -821,7 +780,7 @@ struct soc_enum { unsigned char shift_r; unsigned int max; unsigned int mask; - const char * const *texts; + const char **texts; const unsigned int *values; void *dapm; }; @@ -830,22 +789,21 @@ struct soc_enum { unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); unsigned int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val); -unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec, - unsigned int reg, const void *data, size_t len); - -/* device driver data */ - -static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card, - void *data) +/* platform DAPM IO - refactor */ +static inline unsigned int snd_soc_platform_read(struct snd_soc_platform *platform, + unsigned int reg) { - card->drvdata = data; + return platform->driver->read(platform, reg); } -static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card) +static inline unsigned int snd_soc_platform_write(struct snd_soc_platform *platform, + unsigned int reg, unsigned int val) { - return card->drvdata; + return platform->driver->write(platform, reg, val); } +/* device driver data */ + static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec, void *data) { @@ -879,25 +837,6 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd) return dev_get_drvdata(&rtd->dev); } -static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card) -{ - INIT_LIST_HEAD(&card->dai_dev_list); - INIT_LIST_HEAD(&card->codec_dev_list); - INIT_LIST_HEAD(&card->platform_dev_list); - INIT_LIST_HEAD(&card->widgets); - INIT_LIST_HEAD(&card->paths); - INIT_LIST_HEAD(&card->dapm_list); -} - -int snd_soc_util_init(void); -void snd_soc_util_exit(void); - #include -#ifdef CONFIG_DEBUG_FS -extern struct dentry *snd_soc_debugfs_root; -#endif - -extern const struct dev_pm_ops snd_soc_pm_ops; - #endif diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index e6a6b99..129ee7b 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -366,9 +366,10 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm) } } -static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; if (!card->dev->dma_mask) @@ -376,14 +377,14 @@ static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = DMA_BIT_MASK(64); - if (dai->driver->playback.channels_min) { + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { ret = omap_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) goto out; } - if (dai->driver->capture.channels_min) { + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { ret = omap_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 59abd84..ab0bc4b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -44,12 +44,10 @@ #define NAME_SIZE 32 -static DEFINE_MUTEX(pcm_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS -struct dentry *snd_soc_debugfs_root; -EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); +static struct dentry *debugfs_root; #endif static DEFINE_MUTEX(client_mutex); @@ -58,6 +56,8 @@ static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); +static int snd_soc_register_card(struct snd_soc_card *card); +static int snd_soc_unregister_card(struct snd_soc_card *card); static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); /* @@ -69,73 +69,28 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); -/* returns the minimum number of bytes needed to represent - * a particular given value */ -static int min_bytes_needed(unsigned long val) -{ - int c = 0; - int i; - - for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c) - if (val & (1UL << i)) - break; - c = (sizeof val * 8) - c; - if (!c || (c % 8)) - c = (c + 8) / 8; - else - c /= 8; - return c; -} - -/* fill buf which is 'len' bytes with a formatted - * string of the form 'reg: value\n' */ -static int format_register_str(struct snd_soc_codec *codec, - unsigned int reg, char *buf, size_t len) -{ - int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; - int regsize = codec->driver->reg_word_size * 2; - int ret; - char tmpbuf[len + 1]; - char regbuf[regsize + 1]; - - /* since tmpbuf is allocated on the stack, warn the callers if they - * try to abuse this function */ - WARN_ON(len > 63); - - /* +2 for ': ' and + 1 for '\n' */ - if (wordsize + regsize + 2 + 1 != len) - return -EINVAL; - - ret = snd_soc_read(codec , reg); - if (ret < 0) { - memset(regbuf, 'X', regsize); - regbuf[regsize] = '\0'; - } else { - snprintf(regbuf, regsize + 1, "%.*x", regsize, ret); - } - - /* prepare the buffer */ - snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf); - /* copy it back to the caller without the '\0' */ - memcpy(buf, tmpbuf, len); - - return 0; -} +/* ASoC no host IO hardware. + * TODO: fine tune these values for all host less transfers. + */ +static const struct snd_pcm_hardware no_host_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = PAGE_SIZE >> 2, + .period_bytes_max = PAGE_SIZE >> 1, + .periods_min = 2, + .periods_max = 4, + .buffer_bytes_max = PAGE_SIZE, +}; /* codec register dump */ -static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, - size_t count, loff_t pos) +static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) { - int i, step = 1; - int wordsize, regsize; - int len; - size_t total = 0; - loff_t p = 0; - - wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; - regsize = codec->driver->reg_word_size * 2; - - len = wordsize + regsize + 2 + 1; + int ret, i, step = 1, count = 0; if (!codec->driver->reg_cache_size) return 0; @@ -143,37 +98,55 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, if (codec->driver->reg_cache_step) step = codec->driver->reg_cache_step; + count += sprintf(buf, "%s registers\n", codec->name); for (i = 0; i < codec->driver->reg_cache_size; i += step) { - if (codec->readable_register && !codec->readable_register(codec, i)) + if (codec->driver->readable_register && !codec->driver->readable_register(i)) continue; + + count += sprintf(buf + count, "%2x: ", i); + if (count >= PAGE_SIZE - 1) + break; + if (codec->driver->display_register) { count += codec->driver->display_register(codec, buf + count, PAGE_SIZE - count, i); } else { - /* only support larger than PAGE_SIZE bytes debugfs - * entries for the default case */ - if (p >= pos) { - if (total + len >= count - 1) - break; - format_register_str(codec, i, buf + total, len); - total += len; - } - p += len; + /* If the read fails it's almost certainly due to + * the register being volatile and the device being + * powered off. + */ + ret = snd_soc_read(codec, i); + if (ret >= 0) + count += snprintf(buf + count, + PAGE_SIZE - count, + "%4x", ret); + else + count += snprintf(buf + count, + PAGE_SIZE - count, + "", ret); } + + if (count >= PAGE_SIZE - 1) + break; + + count += snprintf(buf + count, PAGE_SIZE - count, "\n"); + if (count >= PAGE_SIZE - 1) + break; } - total = min(total, count - 1); + /* Truncate count; min() would cause a warning */ + if (count >= PAGE_SIZE) + count = PAGE_SIZE - 1; - return total; + return count; } - static ssize_t codec_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_soc_pcm_runtime *rtd = container_of(dev, struct snd_soc_pcm_runtime, dev); - return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0); + return soc_codec_reg_show(rtd->codec, buf); } static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); @@ -212,28 +185,16 @@ static int codec_reg_open_file(struct inode *inode, struct file *file) } static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { ssize_t ret; struct snd_soc_codec *codec = file->private_data; - char *buf; - - if (*ppos < 0 || !count) - return -EINVAL; - - buf = kmalloc(count, GFP_KERNEL); + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; - - ret = soc_codec_reg_show(codec, buf, count, *ppos); - if (ret >= 0) { - if (copy_to_user(user_buf, buf, ret)) { - kfree(buf); - return -EFAULT; - } - *ppos += ret; - } - + ret = soc_codec_reg_show(codec, buf); + if (ret >= 0) + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); kfree(buf); return ret; } @@ -242,7 +203,7 @@ static ssize_t codec_reg_write_file(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { char buf[32]; - size_t buf_size; + int buf_size; char *start = buf; unsigned long reg, value; int step = 1; @@ -259,14 +220,12 @@ static ssize_t codec_reg_write_file(struct file *file, while (*start == ' ') start++; reg = simple_strtoul(start, &start, 16); + if ((reg >= codec->driver->reg_cache_size) || (reg % step)) + return -EINVAL; while (*start == ' ') start++; if (strict_strtoul(start, 16, &value)) return -EINVAL; - - /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER); - snd_soc_write(codec, reg, value); return buf_size; } @@ -290,11 +249,6 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) return; } - debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root, - &codec->cache_sync); - debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root, - &codec->cache_only); - codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->debugfs_codec_root, codec, &codec_reg_fops); @@ -302,7 +256,13 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) printk(KERN_WARNING "ASoC: Failed to create codec register debugfs file\n"); - snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root); + codec->dapm.debugfs_dapm = debugfs_create_dir("dapm", + codec->debugfs_codec_root); + if (!codec->dapm.debugfs_dapm) + printk(KERN_WARNING + "Failed to create DAPM debugfs directory\n"); + + snd_soc_dapm_debugfs_init(&codec->dapm); } static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) @@ -413,7 +373,7 @@ static const struct file_operations platform_list_fops = { static void soc_init_card_debugfs(struct snd_soc_card *card) { card->debugfs_card_root = debugfs_create_dir(card->name, - snd_soc_debugfs_root); + debugfs_root); if (!card->debugfs_card_root) { dev_warn(card->dev, "ASoC: Failed to create codec debugfs directory\n"); @@ -492,41 +452,135 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret; - if (!codec_dai->driver->symmetric_rates && - !cpu_dai->driver->symmetric_rates && - !rtd->dai_link->symmetric_rates) - return 0; + if (codec_dai->driver->symmetric_rates || cpu_dai->driver->symmetric_rates || + rtd->dai_link->symmetric_rates) { + dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", + rtd->rate); - /* This can happen if multiple streams are starting simultaneously - - * the second can need to get its constraints before the first has - * picked a rate. Complain and allow the application to carry on. - */ - if (!rtd->rate) { - dev_warn(&rtd->dev, - "Not enforcing symmetric_rates due to race\n"); - return 0; + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + rtd->rate, + rtd->rate); + if (ret < 0) { + dev_err(&rtd->dev, + "Unable to apply rate symmetry constraint: %d\n", ret); + return ret; + } } - dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate); + return 0; +} - ret = snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - rtd->rate, rtd->rate); - if (ret < 0) { - dev_err(&rtd->dev, - "Unable to apply rate symmetry constraint: %d\n", ret); - return ret; - } +static int is_be_supported(struct snd_soc_pcm_runtime *rtd, const char *link) +{ + int i; + for (i= 0; i < rtd->dai_link->num_be; i++) { + if(!strcmp(rtd->dai_link->supported_be[i], link)) + return 1; + } return 0; } +int snd_soc_get_backend_dais(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + int i, num; + const char *fe_aif = NULL, *be_aif; + enum snd_soc_dapm_type fe_type, be_type; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + fe_type = snd_soc_dapm_aif_in; + be_type = snd_soc_dapm_aif_out; + } else { + fe_type = snd_soc_dapm_aif_out; + be_type = snd_soc_dapm_aif_in; + } + + /* search card for valid frontend steams */ + for (i = 0; i < card->num_links; i++) { + + /* check for frontend */ + if (card->rtd[i].dai_link->dynamic) + continue; + + fe_aif = snd_soc_dapm_get_aif(&card->rtd[i].platform->dapm, + cpu_dai->driver->name, fe_type); + } + + if (fe_aif == NULL) { + dev_err(&rtd->dev, "no frontend widgets for stream %s\n", + cpu_dai->driver->name); + return 0; + } else + dev_dbg(&rtd->dev, "got fe %s\n", fe_aif); + + /* search card for valid backends */ + for (i = 0; i < card->num_links; i++) { + + /* check for frontend */ + if (card->rtd[i].dai_link->dynamic) + continue; + + /* backends must belong to this frontend */ + if (card->rtd[i].dai_link->no_pcm) { + + if (!is_be_supported(rtd, card->rtd[i].dai_link->name)) + continue; + + be_aif = snd_soc_dapm_get_aif(&card->rtd[i].platform->dapm, + card->rtd[i].dai_link->stream_name, be_type); + if (be_aif == NULL) { + dev_dbg(&rtd->dev, "no backend widget for stream %s\n", + card->rtd[i].dai_link->stream_name); + continue; + } + dev_dbg(&rtd->dev, "got be %s for stream %s\n", be_aif, + card->rtd[i].dai_link->stream_name); + + /* check for valid path */ + num = snd_soc_scenario_set_path(&card->rtd[i].platform->dapm, + fe_aif, be_aif, substream->stream); + + /* add backend if we have space */ + if (num > 0) { + if (rtd->num_be[substream->stream] == SND_SOC_MAX_BE) + dev_dbg(&rtd->dev, "no more backends permitted\n"); + else { + dev_dbg(&rtd->dev, "** active path for %s to %s\n", fe_aif, be_aif); + rtd->be_rtd[rtd->num_be[substream->stream]++][substream->stream] = &card->rtd[i]; + card->rtd[i].fe_clients++; + } + } + } + } + + return rtd->num_be[substream->stream] ? rtd->num_be[substream->stream] : -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_get_backend_dais); + +void snd_soc_put_backend_dais(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i; + + for (i = 0; i < rtd->num_be[substream->stream]; i++) { + rtd->be_rtd[i][substream->stream]->fe_clients--; + rtd->be_rtd[i][substream->stream] = NULL; + } + rtd->num_be[substream->stream] = 0; +} +EXPORT_SYMBOL_GPL(snd_soc_put_backend_dais); + /* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls * startup for the cpu DAI, platform, machine and codec DAI. */ -static int soc_pcm_open(struct snd_pcm_substream *substream) + +int snd_soc_pcm_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -537,7 +591,33 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; int ret = 0; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); + + /* Work out backend DAI's if we are a frontend */ + if (rtd->dai_link->dynamic) { + ret = snd_soc_get_backend_dais(substream); + if (ret < 0) { + printk(KERN_ERR "asoc: no valid backend routes for PCM: %s\n", + dev_name(&rtd->dev)); + goto out; + } + } + + /* Are we the backend and already enabled */ + if (rtd->dai_link->no_pcm) { + + if (rtd->fe_clients == 0) { + dev_err(&rtd->dev, "operations not permitted on backend DAI\n"); + ret = -ENODEV; + goto out; + } + + if (rtd->be_active++) + goto no_pcm; + } + + if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) + snd_soc_set_runtime_hwparams(substream, &no_host_hardware); /* startup the audio subsystem */ if (cpu_dai->driver->ops->startup) { @@ -549,7 +629,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->open) { + if (platform->driver->ops->open) { ret = platform->driver->ops->open(substream); if (ret < 0) { printk(KERN_ERR "asoc: can't open platform %s\n", platform->name); @@ -574,6 +654,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } + if (rtd->dai_link->no_pcm) + goto no_pcm; + /* Check that the codec and cpu DAIs are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { runtime->hw.rate_min = @@ -623,7 +706,6 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) runtime->hw.rates |= codec_dai_drv->capture.rates; } - ret = -EINVAL; snd_pcm_limit_hw_rates(runtime); if (!runtime->hw.rates) { printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", @@ -635,8 +717,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) codec_dai->name, cpu_dai->name); goto config_err; } - if (!runtime->hw.channels_min || !runtime->hw.channels_max || - runtime->hw.channels_min > runtime->hw.channels_max) { + if (!runtime->hw.channels_min || !runtime->hw.channels_max) { printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", codec_dai->name, cpu_dai->name); goto config_err; @@ -656,7 +737,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) runtime->hw.channels_max); pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, runtime->hw.rate_max); - +no_pcm: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { cpu_dai->playback_active++; codec_dai->playback_active++; @@ -667,7 +748,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) cpu_dai->active++; codec_dai->active++; rtd->codec->active++; - mutex_unlock(&pcm_mutex); + + mutex_unlock(&rtd->pcm_mutex); + return 0; config_err: @@ -679,16 +762,21 @@ machine_err: codec_dai->driver->ops->shutdown(substream, codec_dai); codec_dai_err: - if (platform->driver->ops && platform->driver->ops->close) + if (platform->driver->ops->close) platform->driver->ops->close(substream); platform_err: if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); out: - mutex_unlock(&pcm_mutex); + if (rtd->dai_link->dynamic) + snd_soc_put_backend_dais(substream); + + mutex_unlock(&rtd->pcm_mutex); + return ret; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_open); /* * Power down the audio subsystem pmdown_time msecs after close is called. @@ -700,8 +788,9 @@ static void close_delayed_work(struct work_struct *work) struct snd_soc_pcm_runtime *rtd = container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); pr_debug("pop wq checking: %s status: %s waiting: %s\n", codec_dai->driver->playback.stream_name, @@ -711,12 +800,17 @@ static void close_delayed_work(struct work_struct *work) /* are we waiting on this codec DAI stream */ if (codec_dai->pop_wait == 1) { codec_dai->pop_wait = 0; - snd_soc_dapm_stream_event(rtd, - codec_dai->driver->playback.stream_name, - SND_SOC_DAPM_STREAM_STOP); + if (rtd->dai_link->dynamic) + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + cpu_dai->driver->playback.stream_name, + SND_SOC_DAPM_STREAM_STOP); + else + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + codec_dai->driver->playback.stream_name, + SND_SOC_DAPM_STREAM_STOP); } - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); } /* @@ -724,7 +818,7 @@ static void close_delayed_work(struct work_struct *work) * freed here. The cpu DAI, codec DAI, machine and platform are also * shutdown. */ -static int soc_codec_close(struct snd_pcm_substream *substream) +int snd_soc_pcm_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -732,7 +826,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { cpu_dai->playback_active--; @@ -746,6 +840,9 @@ static int soc_codec_close(struct snd_pcm_substream *substream) codec_dai->active--; codec->active--; + if (rtd->dai_link->no_pcm) + rtd->be_active--; + /* Muting the DAC suppresses artifacts caused during digital * shutdown, for example from stopping clocks. */ @@ -761,7 +858,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream) if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); - if (platform->driver->ops && platform->driver->ops->close) + if (platform->driver->ops->close) platform->driver->ops->close(substream); cpu_dai->runtime = NULL; @@ -772,21 +869,30 @@ static int soc_codec_close(struct snd_pcm_substream *substream) msecs_to_jiffies(rtd->pmdown_time)); } else { /* capture streams can be powered down now */ - snd_soc_dapm_stream_event(rtd, - codec_dai->driver->capture.stream_name, - SND_SOC_DAPM_STREAM_STOP); + if (rtd->dai_link->dynamic) + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, + cpu_dai->driver->capture.stream_name, + SND_SOC_DAPM_STREAM_STOP); + else + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, + codec_dai->driver->capture.stream_name, + SND_SOC_DAPM_STREAM_STOP); } - mutex_unlock(&pcm_mutex); + if (rtd->dai_link->dynamic) + snd_soc_put_backend_dais(substream); + + mutex_unlock(&rtd->pcm_mutex); return 0; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_close); /* * Called by ALSA when the PCM substream is prepared, can set format, sample * rate, etc. This function is non atomic and can be called multiple times, * it can refer to the runtime info. */ -static int soc_pcm_prepare(struct snd_pcm_substream *substream) +int snd_soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -794,7 +900,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { ret = rtd->dai_link->ops->prepare(substream); @@ -804,7 +910,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->prepare) { + if (platform->driver->ops->prepare) { ret = platform->driver->ops->prepare(substream); if (ret < 0) { printk(KERN_ERR "asoc: platform prepare error\n"); @@ -835,28 +941,39 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) cancel_delayed_work(&rtd->delayed_work); } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dapm_stream_event(rtd, + if (rtd->dai_link->dynamic) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + cpu_dai->driver->playback.stream_name, + SND_SOC_DAPM_STREAM_START); + else + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, + cpu_dai->driver->capture.stream_name, + SND_SOC_DAPM_STREAM_START); + } else { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, codec_dai->driver->playback.stream_name, SND_SOC_DAPM_STREAM_START); - else - snd_soc_dapm_stream_event(rtd, + else + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, codec_dai->driver->capture.stream_name, SND_SOC_DAPM_STREAM_START); - + } snd_soc_dai_digital_mute(codec_dai, 0); out: - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_prepare); /* * Called by ALSA when the hardware params are set by application. This * function can also be called multiple times and can allocate buffers * (using snd_pcm_lib_* ). It's non-atomic. */ -static int soc_pcm_hw_params(struct snd_pcm_substream *substream, +int snd_soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -865,7 +982,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { ret = rtd->dai_link->ops->hw_params(substream, params); @@ -893,7 +1010,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } } - if (platform->driver->ops && platform->driver->ops->hw_params) { + if (platform->driver->ops->hw_params) { ret = platform->driver->ops->hw_params(substream, params); if (ret < 0) { printk(KERN_ERR "asoc: platform %s hw params failed\n", @@ -904,8 +1021,21 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, rtd->rate = params_rate(params); + /* malloc a page for hostless IO. + * FIXME: rework with alsa-lib changes so that this malloc is not required. + */ + if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) { + substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV; + substream->dma_buffer.dev.dev = &rtd->dev; + substream->dma_buffer.private_data = NULL; + + ret = snd_pcm_lib_malloc_pages(substream, PAGE_SIZE); + if (ret < 0) + goto platform_err; + } + out: - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex); return ret; platform_err: @@ -920,14 +1050,15 @@ codec_err: if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) rtd->dai_link->ops->hw_free(substream); - mutex_unlock(&pcm_mutex); + mutex_unlock(&rtd->pcm_mutex);; return ret; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_hw_params); /* * Frees resources allocated by hw_params, can be called multiple times */ -static int soc_pcm_hw_free(struct snd_pcm_substream *substream) +int snd_soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -935,7 +1066,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; - mutex_lock(&pcm_mutex); + mutex_lock(&rtd->pcm_mutex); /* apply codec digital mute */ if (!codec->active) @@ -946,7 +1077,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) rtd->dai_link->ops->hw_free(substream); /* free any DMA resources */ - if (platform->driver->ops && platform->driver->ops->hw_free) + if (platform->driver->ops->hw_free) platform->driver->ops->hw_free(substream); /* now free hw params for the DAIs */ @@ -956,11 +1087,15 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); - mutex_unlock(&pcm_mutex); + if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) + snd_pcm_lib_free_pages(substream); + + mutex_unlock(&rtd->pcm_mutex); return 0; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_hw_free); -static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +int snd_soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -974,7 +1109,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } - if (platform->driver->ops && platform->driver->ops->trigger) { + if (platform->driver->ops->trigger) { ret = platform->driver->ops->trigger(substream, cmd); if (ret < 0) return ret; @@ -987,13 +1122,14 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } return 0; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_trigger); /* * soc level wrapper for pointer callback * If cpu_dai, codec_dai, platform driver has the delay callback, than * the runtime->delay will be updated accordingly. */ -static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) +snd_pcm_uframes_t snd_soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; @@ -1003,7 +1139,7 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_uframes_t offset = 0; snd_pcm_sframes_t delay = 0; - if (platform->driver->ops && platform->driver->ops->pointer) + if (platform->driver->ops->pointer) offset = platform->driver->ops->pointer(substream); if (cpu_dai->driver->ops->delay) @@ -1019,23 +1155,55 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) return offset; } +EXPORT_SYMBOL_GPL(snd_soc_pcm_pointer); -/* ASoC PCM operations */ -static struct snd_pcm_ops soc_pcm_ops = { - .open = soc_pcm_open, - .close = soc_codec_close, - .hw_params = soc_pcm_hw_params, - .hw_free = soc_pcm_hw_free, - .prepare = soc_pcm_prepare, - .trigger = soc_pcm_trigger, - .pointer = soc_pcm_pointer, -}; -#ifdef CONFIG_PM_SLEEP +int snd_soc_pcm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_platform *platform = rtd->platform; + + if (platform->driver->ops->ioctl) + return platform->driver->ops->ioctl(substream, cmd, arg); + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, + const char *dai_link, int stream) +{ + int i; + + for (i = 0; i < card->num_links; i++) { + if (card->rtd[i].dai_link->no_pcm && + !strcmp(card->rtd[i].dai_link->name, dai_link)) + return card->rtd[i].pcm->streams[stream].substream; + } + dev_dbg(card->dev, "failed to find dai link %s\n", dai_link); + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); + +struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, + const char *dai_link) +{ + int i; + + for (i = 0; i < card->num_links; i++) { + if (!strcmp(card->rtd[i].dai_link->name, dai_link)) + return &card->rtd[i]; + } + dev_dbg(card->dev, "failed to find rtd %s\n", dai_link); + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); + +#ifdef CONFIG_PM /* powers down audio subsystem for suspend */ -int snd_soc_suspend(struct device *dev) +static int soc_suspend(struct device *dev) { - struct snd_soc_card *card = dev_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct snd_soc_card *card = platform_get_drvdata(pdev); struct snd_soc_codec *codec; int i; @@ -1076,7 +1244,7 @@ int snd_soc_suspend(struct device *dev) } if (card->suspend_pre) - card->suspend_pre(card); + card->suspend_pre(pdev, PMSG_SUSPEND); for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; @@ -1106,12 +1274,12 @@ int snd_soc_suspend(struct device *dev) continue; if (driver->playback.stream_name != NULL) - snd_soc_dapm_stream_event(&card->rtd[i], driver->playback.stream_name, - SND_SOC_DAPM_STREAM_SUSPEND); + snd_soc_dapm_stream_event(&card->rtd[i], SNDRV_PCM_STREAM_PLAYBACK, + driver->playback.stream_name, SND_SOC_DAPM_STREAM_SUSPEND); if (driver->capture.stream_name != NULL) - snd_soc_dapm_stream_event(&card->rtd[i], driver->capture.stream_name, - SND_SOC_DAPM_STREAM_SUSPEND); + snd_soc_dapm_stream_event(&card->rtd[i], SNDRV_PCM_STREAM_CAPTURE, + driver->capture.stream_name, SND_SOC_DAPM_STREAM_SUSPEND); } /* suspend all CODECs */ @@ -1124,7 +1292,6 @@ int snd_soc_suspend(struct device *dev) case SND_SOC_BIAS_OFF: codec->driver->suspend(codec, PMSG_SUSPEND); codec->suspended = 1; - codec->cache_sync = 1; break; default: dev_dbg(codec->dev, "CODEC is on over suspend\n"); @@ -1144,11 +1311,10 @@ int snd_soc_suspend(struct device *dev) } if (card->suspend_post) - card->suspend_post(card); + card->suspend_post(pdev, PMSG_SUSPEND); return 0; } -EXPORT_SYMBOL_GPL(snd_soc_suspend); /* deferred resume work, so resume can complete before we finished * setting our codec back up, which can be very slow on I2C @@ -1157,6 +1323,7 @@ static void soc_resume_deferred(struct work_struct *work) { struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); + struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; int i; @@ -1170,7 +1337,7 @@ static void soc_resume_deferred(struct work_struct *work) snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); if (card->resume_pre) - card->resume_pre(card); + card->resume_pre(pdev); /* resume AC97 DAIs */ for (i = 0; i < card->num_rtd; i++) { @@ -1209,12 +1376,12 @@ static void soc_resume_deferred(struct work_struct *work) continue; if (driver->playback.stream_name != NULL) - snd_soc_dapm_stream_event(&card->rtd[i], driver->playback.stream_name, - SND_SOC_DAPM_STREAM_RESUME); + snd_soc_dapm_stream_event(&card->rtd[i], SNDRV_PCM_STREAM_PLAYBACK, + driver->playback.stream_name, SND_SOC_DAPM_STREAM_RESUME); if (driver->capture.stream_name != NULL) - snd_soc_dapm_stream_event(&card->rtd[i], driver->capture.stream_name, - SND_SOC_DAPM_STREAM_RESUME); + snd_soc_dapm_stream_event(&card->rtd[i], SNDRV_PCM_STREAM_CAPTURE, + driver->capture.stream_name, SND_SOC_DAPM_STREAM_RESUME); } /* unmute any active DACs */ @@ -1245,7 +1412,7 @@ static void soc_resume_deferred(struct work_struct *work) } if (card->resume_post) - card->resume_post(card); + card->resume_post(pdev); dev_dbg(card->dev, "resume work completed\n"); @@ -1254,9 +1421,10 @@ static void soc_resume_deferred(struct work_struct *work) } /* powers up audio subsystem after a suspend */ -int snd_soc_resume(struct device *dev) +static int soc_resume(struct device *dev) { - struct snd_soc_card *card = dev_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct snd_soc_card *card = platform_get_drvdata(pdev); int i; /* AC97 devices might have other drivers hanging off them so @@ -1269,24 +1437,46 @@ int snd_soc_resume(struct device *dev) if (cpu_dai->driver->ac97_control) { dev_dbg(dev, "Resuming AC97 immediately\n"); soc_resume_deferred(&card->deferred_resume_work); - } else { - dev_dbg(dev, "Scheduling resume work\n"); - if (!schedule_work(&card->deferred_resume_work)) - dev_err(dev, "resume work item may be lost\n"); + return 0; } } + dev_dbg(dev, "Scheduling resume work\n"); + if (!schedule_work(&card->deferred_resume_work)) + dev_err(dev, "resume work item may be lost\n"); return 0; } -EXPORT_SYMBOL_GPL(snd_soc_resume); #else -#define snd_soc_suspend NULL -#define snd_soc_resume NULL +#define soc_suspend NULL +#define soc_resume NULL #endif +#define NULL_FORMATS \ + (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |\ + SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |\ + SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32) + static struct snd_soc_dai_ops null_dai_ops = { }; +static struct snd_soc_dai_driver null_codec_dai_drv = { + .name = "null-codec-dai", + .ops = &null_dai_ops, + .capture = { + .channels_min = 1 , + .channels_max = 16, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = NULL_FORMATS, + }, + .playback = { + .channels_min = 1 , + .channels_max = 16, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = NULL_FORMATS, + }, +}; +static struct snd_soc_codec_driver null_codec_drv = {}; + static int soc_bind_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; @@ -1294,7 +1484,6 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) struct snd_soc_codec *codec; struct snd_soc_platform *platform; struct snd_soc_dai *codec_dai, *cpu_dai; - const char *platform_name; if (rtd->complete) return 1; @@ -1307,6 +1496,10 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) /* no, then find CPU DAI from registered DAIs*/ list_for_each_entry(cpu_dai, &dai_list, list) { if (!strcmp(cpu_dai->name, dai_link->cpu_dai_name)) { + + if (!try_module_get(cpu_dai->dev->driver->owner)) + return -ENODEV; + rtd->cpu_dai = cpu_dai; goto find_codec; } @@ -1327,7 +1520,7 @@ find_codec: /* CODEC found, so find CODEC DAI from registered DAIs from this CODEC*/ list_for_each_entry(codec_dai, &dai_list, list) { - if (codec->dev == codec_dai->dev && + if ((codec->dev == codec_dai->dev || codec->driver == &null_codec_drv) && !strcmp(codec_dai->name, dai_link->codec_dai_name)) { rtd->codec_dai = codec_dai; goto find_platform; @@ -1343,18 +1536,13 @@ find_codec: dai_link->codec_name); find_platform: - /* do we need a platform? */ - if (rtd->platform) + /* do we already have the CODEC DAI for this link ? */ + if (rtd->platform) { goto out; - - /* if there's no platform we match on the empty platform */ - platform_name = dai_link->platform_name; - if (!platform_name) - platform_name = "snd-soc-dummy"; - - /* no, then find one from the set of registered platforms */ + } + /* no, then find CPU DAI from registered DAIs*/ list_for_each_entry(platform, &platform_list, list) { - if (!strcmp(platform->name, platform_name)) { + if (!strcmp(platform->name, dai_link->platform_name)) { rtd->platform = platform; goto out; } @@ -1428,6 +1616,10 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) if (err < 0) printk(KERN_ERR "asoc: failed to remove %s\n", platform->name); } + + /* Make sure all DAPM widgets are freed */ + snd_soc_dapm_free(&platform->dapm); + platform->probed = 0; list_del(&platform->card_list); module_put(platform->dev->driver->owner); @@ -1450,16 +1642,6 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) } } -static void soc_remove_dai_links(struct snd_soc_card *card) -{ - int i; - - for (i = 0; i < card->num_rtd; i++) - soc_remove_dai_link(card, i); - - card->num_rtd = 0; -} - static void soc_set_name_prefix(struct snd_soc_card *card, struct snd_soc_codec *codec) { @@ -1481,49 +1663,31 @@ static int soc_probe_codec(struct snd_soc_card *card, struct snd_soc_codec *codec) { int ret = 0; - const struct snd_soc_codec_driver *driver = codec->driver; codec->card = card; codec->dapm.card = card; soc_set_name_prefix(card, codec); - if (!try_module_get(codec->dev->driver->owner)) - return -ENODEV; - - soc_init_codec_debugfs(codec); - - if (driver->dapm_widgets) - snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, - driver->num_dapm_widgets); - - if (driver->probe) { - ret = driver->probe(codec); + if (codec->driver->probe) { + ret = codec->driver->probe(codec); if (ret < 0) { dev_err(codec->dev, "asoc: failed to probe CODEC %s: %d\n", codec->name, ret); - goto err_probe; + return ret; } } - if (driver->controls) - snd_soc_add_controls(codec, driver->controls, - driver->num_controls); - if (driver->dapm_routes) - snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, - driver->num_dapm_routes); + soc_init_codec_debugfs(codec); /* mark codec as probed and add to card codec list */ + if (!try_module_get(codec->dev->driver->owner)) + return -ENODEV; + codec->probed = 1; list_add(&codec->card_list, &card->codec_dev_list); list_add(&codec->dapm.list, &card->dapm_list); - return 0; - -err_probe: - soc_cleanup_codec_debugfs(codec); - module_put(codec->dev->driver->owner); - return ret; } @@ -1548,7 +1712,6 @@ static int soc_post_component_init(struct snd_soc_card *card, rtd = &card->rtd_aux[num]; name = aux_dev->name; } - rtd->card = card; /* machine controls, routes and widgets are not prefixed */ temp = codec->name_prefix; @@ -1567,9 +1730,11 @@ static int soc_post_component_init(struct snd_soc_card *card, /* Make sure all DAPM widgets are instantiated */ snd_soc_dapm_new_widgets(&codec->dapm); + snd_soc_dapm_sync(&codec->dapm); /* register the rtd device */ rtd->codec = codec; + rtd->card = card; rtd->dev.parent = card->dev; rtd->dev.release = rtd_release; rtd->dev.init_name = name; @@ -1610,27 +1775,31 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* config components */ codec_dai->codec = codec; + platform->card = card; cpu_dai->platform = platform; codec_dai->card = card; cpu_dai->card = card; + codec->card = card; + platform->dapm.card = card; + rtd->card = card; + rtd->dev.parent = card->dev; /* set default power off timeout */ rtd->pmdown_time = pmdown_time; /* probe the cpu_dai */ if (!cpu_dai->probed) { - if (!try_module_get(cpu_dai->dev->driver->owner)) - return -ENODEV; - if (cpu_dai->driver->probe) { ret = cpu_dai->driver->probe(cpu_dai); if (ret < 0) { printk(KERN_ERR "asoc: failed to probe CPU DAI %s\n", cpu_dai->name); - module_put(cpu_dai->dev->driver->owner); return ret; } } + /* Make sure all DAPM widgets are instantiated */ + snd_soc_dapm_new_widgets(&platform->dapm); + cpu_dai->probed = 1; /* mark cpu_dai as probed and add to card cpu_dai list */ list_add(&cpu_dai->card_list, &card->dai_dev_list); @@ -1645,21 +1814,22 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* probe the platform */ if (!platform->probed) { - if (!try_module_get(platform->dev->driver->owner)) - return -ENODEV; - if (platform->driver->probe) { ret = platform->driver->probe(platform); if (ret < 0) { printk(KERN_ERR "asoc: failed to probe platform %s\n", platform->name); - module_put(platform->dev->driver->owner); return ret; } } /* mark platform as probed and add to card platform list */ + + if (!try_module_get(platform->dev->driver->owner)) + return -ENODEV; + platform->probed = 1; list_add(&platform->card_list, &card->platform_dev_list); + INIT_LIST_HEAD(&platform->dapm.list); } /* probe the CODEC DAI */ @@ -1680,6 +1850,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + mutex_init(&rtd->pcm_mutex); ret = soc_post_component_init(card, codec, num, 0); if (ret) @@ -1815,6 +1986,7 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, static void snd_soc_instantiate_card(struct snd_soc_card *card) { + struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; struct snd_soc_codec_conf *codec_conf; enum snd_soc_compress_type compress_type; @@ -1831,6 +2003,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) for (i = 0; i < card->num_links; i++) soc_bind_dai_link(card, i); + printk(KERN_INFO "card->num_rtd: %d, card->num_links: %d\n", card->num_rtd, card->num_links); + /* bind completed ? */ if (card->num_rtd != card->num_links) { mutex_unlock(&card->mutex); @@ -1841,8 +2015,6 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) list_for_each_entry(codec, &codec_list, list) { if (codec->cache_init) continue; - /* by default we don't override the compress_type */ - compress_type = 0; /* check to see if we need to override the compress_type */ for (i = 0; i < card->num_configs; ++i) { codec_conf = &card->codec_conf[i]; @@ -1853,6 +2025,18 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) break; } } + if (i == card->num_configs) { + /* no need to override the compress_type so + * go ahead and do the standard thing */ + ret = snd_soc_init_codec_cache(codec, 0); + if (ret < 0) { + mutex_unlock(&card->mutex); + return; + } + continue; + } + /* override the compress_type with the one supplied in + * the machine driver */ ret = snd_soc_init_codec_cache(codec, compress_type); if (ret < 0) { mutex_unlock(&card->mutex); @@ -1871,27 +2055,14 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } card->snd_card->dev = card->dev; - card->dapm.bias_level = SND_SOC_BIAS_OFF; - card->dapm.dev = card->dev; - card->dapm.card = card; - list_add(&card->dapm.list, &card->dapm_list); - -#ifdef CONFIG_DEBUG_FS - snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); -#endif - -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM /* deferred resume work */ INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); #endif - if (card->dapm_widgets) - snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, - card->num_dapm_widgets); - /* initialise the sound card only once */ if (card->probe) { - ret = card->probe(card); + ret = card->probe(pdev); if (ret < 0) goto card_probe_error; } @@ -1914,34 +2085,12 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } - /* We should have a non-codec control add function but we don't */ - if (card->controls) - snd_soc_add_controls(list_first_entry(&card->codec_dev_list, - struct snd_soc_codec, - card_list), - card->controls, - card->num_controls); - - if (card->dapm_routes) - snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, - card->num_dapm_routes); - snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), "%s", card->name); snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), - "%s", card->long_name ? card->long_name : card->name); - if (card->driver_name) - strlcpy(card->snd_card->driver, card->driver_name, - sizeof(card->snd_card->driver)); - - if (card->late_probe) { - ret = card->late_probe(card); - if (ret < 0) { - dev_err(card->dev, "%s late_probe() failed: %d\n", - card->name, ret); - goto probe_aux_dev_err; - } - } + "%s", card->long_name); + snprintf(card->snd_card->driver, sizeof(card->snd_card->driver), + "%s", card->name); ret = snd_card_register(card->snd_card); if (ret < 0) { @@ -1971,11 +2120,12 @@ probe_aux_dev_err: soc_remove_aux_dev(card, i); probe_dai_err: - soc_remove_dai_links(card); + for (i = 0; i < card->num_links; i++) + soc_remove_dai_link(card, i); card_probe_error: if (card->remove) - card->remove(card); + card->remove(pdev); snd_card_free(card->snd_card); @@ -1999,15 +2149,16 @@ static int soc_probe(struct platform_device *pdev) struct snd_soc_card *card = platform_get_drvdata(pdev); int ret = 0; - /* - * no card, so machine driver should be registering card - * we should not be here in that case so ret error - */ - if (!card) - return -EINVAL; - /* Bodge while we unpick instantiation */ card->dev = &pdev->dev; + INIT_LIST_HEAD(&card->dai_dev_list); + INIT_LIST_HEAD(&card->codec_dev_list); + INIT_LIST_HEAD(&card->platform_dev_list); + INIT_LIST_HEAD(&card->widgets); + INIT_LIST_HEAD(&card->paths); + INIT_LIST_HEAD(&card->dapm_list); + + soc_init_card_debugfs(card); ret = snd_soc_register_card(card); if (ret != 0) { @@ -2018,49 +2169,45 @@ static int soc_probe(struct platform_device *pdev) return 0; } -static int soc_cleanup_card_resources(struct snd_soc_card *card) +/* removes a socdev */ +static int soc_remove(struct platform_device *pdev) { + struct snd_soc_card *card = platform_get_drvdata(pdev); int i; - /* make sure any delayed work runs */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; - flush_delayed_work_sync(&rtd->delayed_work); - } - - /* remove auxiliary devices */ - for (i = 0; i < card->num_aux_devs; i++) - soc_remove_aux_dev(card, i); - - /* remove and free each DAI */ - soc_remove_dai_links(card); + if (card->instantiated) { - soc_cleanup_card_debugfs(card); + /* make sure any delayed work runs */ + for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + flush_delayed_work_sync(&rtd->delayed_work); + } - /* remove the card */ - if (card->remove) - card->remove(card); + /* remove auxiliary devices */ + for (i = 0; i < card->num_aux_devs; i++) + soc_remove_aux_dev(card, i); - snd_soc_dapm_free(&card->dapm); + /* remove and free each DAI */ + for (i = 0; i < card->num_rtd; i++) + soc_remove_dai_link(card, i); - kfree(card->rtd); - snd_card_free(card->snd_card); - return 0; + soc_cleanup_card_debugfs(card); -} - -/* removes a socdev */ -static int soc_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); + /* remove the card */ + if (card->remove) + card->remove(pdev); + kfree(card->rtd); + snd_card_free(card->snd_card); + } snd_soc_unregister_card(card); return 0; } -int snd_soc_poweroff(struct device *dev) +static int soc_poweroff(struct device *dev) { - struct snd_soc_card *card = dev_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct snd_soc_card *card = platform_get_drvdata(pdev); int i; if (!card->instantiated) @@ -2077,21 +2224,19 @@ int snd_soc_poweroff(struct device *dev) return 0; } -EXPORT_SYMBOL_GPL(snd_soc_poweroff); -const struct dev_pm_ops snd_soc_pm_ops = { - .suspend = snd_soc_suspend, - .resume = snd_soc_resume, - .poweroff = snd_soc_poweroff, +static const struct dev_pm_ops soc_pm_ops = { + .suspend = soc_suspend, + .resume = soc_resume, + .poweroff = soc_poweroff, }; -EXPORT_SYMBOL_GPL(snd_soc_pm_ops); /* ASoC platform driver */ static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", .owner = THIS_MODULE, - .pm = &snd_soc_pm_ops, + .pm = &soc_pm_ops, }, .probe = soc_probe, .remove = soc_remove, @@ -2104,6 +2249,7 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_pcm_substream *substream[2]; struct snd_pcm *pcm; char new_name[64]; int ret = 0, playback = 0, capture = 0; @@ -2112,10 +2258,17 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) snprintf(new_name, sizeof(new_name), "%s %s-%d", rtd->dai_link->stream_name, codec_dai->name, num); - if (codec_dai->driver->playback.channels_min) - playback = 1; - if (codec_dai->driver->capture.channels_min) - capture = 1; + if (rtd->dai_link->dynamic) { + if (rtd->dai_link->fe_playback_channels) + playback = 1; + if (rtd->dai_link->fe_capture_channels) + capture = 1; + } else { + if (codec_dai->driver->playback.channels_min) + playback = 1; + if (codec_dai->driver->capture.channels_min) + capture = 1; + } dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name); ret = snd_pcm_new(rtd->card->snd_card, new_name, @@ -2127,31 +2280,64 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->pcm = pcm; pcm->private_data = rtd; - if (platform->driver->ops) { - soc_pcm_ops.mmap = platform->driver->ops->mmap; - soc_pcm_ops.pointer = platform->driver->ops->pointer; - soc_pcm_ops.ioctl = platform->driver->ops->ioctl; - soc_pcm_ops.copy = platform->driver->ops->copy; - soc_pcm_ops.silence = platform->driver->ops->silence; - soc_pcm_ops.ack = platform->driver->ops->ack; - soc_pcm_ops.page = platform->driver->ops->page; + substream[SNDRV_PCM_STREAM_PLAYBACK] = + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + substream[SNDRV_PCM_STREAM_CAPTURE] = + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + + if (rtd->dai_link->no_pcm) { + if (playback) + substream[SNDRV_PCM_STREAM_PLAYBACK]->private_data = rtd; + if (capture) + substream[SNDRV_PCM_STREAM_CAPTURE]->private_data = rtd; + goto out; + } + + /* setup any hostless PCMs - i.e. no host IO is performed */ + if (rtd->dai_link->no_host_mode) { + if (substream[SNDRV_PCM_STREAM_PLAYBACK]) { + substream[SNDRV_PCM_STREAM_PLAYBACK]->hw_no_buffer = 1; + snd_soc_set_runtime_hwparams(substream[SNDRV_PCM_STREAM_PLAYBACK], + &no_host_hardware); + } + if (substream[SNDRV_PCM_STREAM_CAPTURE]) { + substream[SNDRV_PCM_STREAM_CAPTURE]->hw_no_buffer = 1; + snd_soc_set_runtime_hwparams(substream[SNDRV_PCM_STREAM_CAPTURE], + &no_host_hardware); + } } + /* ASoC PCM operations */ + rtd->ops.open = snd_soc_pcm_open; + rtd->ops.hw_params = snd_soc_pcm_hw_params; + rtd->ops.prepare = snd_soc_pcm_prepare; + rtd->ops.trigger = snd_soc_pcm_trigger; + rtd->ops.hw_free = snd_soc_pcm_hw_free; + rtd->ops.close = snd_soc_pcm_close; + rtd->ops.pointer = snd_soc_pcm_pointer; + rtd->ops.ioctl = snd_soc_pcm_ioctl; + rtd->ops.ack = platform->driver->ops->ack; + rtd->ops.copy = platform->driver->ops->copy; + rtd->ops.silence = platform->driver->ops->silence; + rtd->ops.page = platform->driver->ops->page; + rtd->ops.mmap = platform->driver->ops->mmap; + if (playback) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops); if (capture) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); - if (platform->driver->pcm_new) { - ret = platform->driver->pcm_new(rtd->card->snd_card, - codec_dai, pcm); - if (ret < 0) { - pr_err("asoc: platform pcm constructor failed\n"); - return ret; - } + if (!platform->driver->pcm_new) + goto out; + ret = platform->driver->pcm_new(rtd); + if (ret < 0) { + printk(KERN_ERR "asoc: platform pcm constructor failed\n"); + return ret; } +out: + pcm->private_free = platform->driver->pcm_free; printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); @@ -2166,53 +2352,16 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) * * Boolean function indiciating if a CODEC register is volatile. */ -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, - unsigned int reg) +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) { - if (codec->volatile_register) - return codec->volatile_register(codec, reg); + if (codec->driver->volatile_register) + return codec->driver->volatile_register(reg); else return 0; } EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register); /** - * snd_soc_codec_readable_register: Report if a register is readable. - * - * @codec: CODEC to query. - * @reg: Register to query. - * - * Boolean function indicating if a CODEC register is readable. - */ -int snd_soc_codec_readable_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - if (codec->readable_register) - return codec->readable_register(codec, reg); - else - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_readable_register); - -/** - * snd_soc_codec_writable_register: Report if a register is writable. - * - * @codec: CODEC to query. - * @reg: Register to query. - * - * Boolean function indicating if a CODEC register is writable. - */ -int snd_soc_codec_writable_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - if (codec->writable_register) - return codec->writable_register(codec, reg); - else - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register); - -/** * snd_soc_new_ac97_codec - initailise AC97 device * @codec: audio codec * @ops: AC97 bus operations @@ -2294,13 +2443,6 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_write); -unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec, - unsigned int reg, const void *data, size_t len) -{ - return codec->bulk_write_raw(codec, reg, data, len); -} -EXPORT_SYMBOL_GPL(snd_soc_bulk_write_raw); - /** * snd_soc_update_bits - update codec register bits * @codec: audio codec @@ -2310,27 +2452,19 @@ EXPORT_SYMBOL_GPL(snd_soc_bulk_write_raw); * * Writes new register value. * - * Returns 1 for change, 0 for no change, or negative error code. + * Returns 1 for change else 0. */ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, unsigned int mask, unsigned int value) { int change; unsigned int old, new; - int ret; - ret = snd_soc_read(codec, reg); - if (ret < 0) - return ret; - - old = ret; + old = snd_soc_read(codec, reg); new = (old & ~mask) | value; change = old != new; - if (change) { - ret = snd_soc_write(codec, reg, new); - if (ret < 0) - return ret; - } + if (change) + snd_soc_write(codec, reg, new); return change; } @@ -2398,6 +2532,10 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, const struct snd_pcm_hardware *hw) { struct snd_pcm_runtime *runtime = substream->runtime; + + if (!runtime) + return 0; + runtime->hw.info = hw->info; runtime->hw.formats = hw->formats; runtime->hw.period_bytes_min = hw->period_bytes_min; @@ -2415,45 +2553,22 @@ EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); * @_template: control template * @data: control private data * @long_name: control long name - * @prefix: control name prefix * * Create a new mixer control from a template control. * * Returns 0 for success, else error. */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, - void *data, char *long_name, - const char *prefix) + void *data, char *long_name) { struct snd_kcontrol_new template; - struct snd_kcontrol *kcontrol; - char *name = NULL; - int name_len; memcpy(&template, _template, sizeof(template)); - template.index = 0; - - if (!long_name) - long_name = template.name; - - if (prefix) { - name_len = strlen(long_name) + strlen(prefix) + 2; - name = kmalloc(name_len, GFP_ATOMIC); - if (!name) - return NULL; - - snprintf(name, name_len, "%s %s", prefix, long_name); - - template.name = name; - } else { + if (long_name) template.name = long_name; - } - - kcontrol = snd_ctl_new1(&template, data); - - kfree(name); + template.index = 0; - return kcontrol; + return snd_ctl_new1(&template, data); } EXPORT_SYMBOL_GPL(snd_soc_cnew); @@ -2472,16 +2587,22 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls) { struct snd_card *card = codec->card->snd_card; + char prefixed_name[44], *name; int err, i; for (i = 0; i < num_controls; i++) { const struct snd_kcontrol_new *control = &controls[i]; - err = snd_ctl_add(card, snd_soc_cnew(control, codec, - control->name, - codec->name_prefix)); + if (codec->name_prefix) { + snprintf(prefixed_name, sizeof(prefixed_name), "%s %s", + codec->name_prefix, control->name); + name = prefixed_name; + } else { + name = control->name; + } + err = snd_ctl_add(card, snd_soc_cnew(control, codec, name)); if (err < 0) { dev_err(codec->dev, "%s: Failed to add %s: %d\n", - codec->name, control->name, err); + codec->name, name, err); return err; } } @@ -2491,6 +2612,35 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, EXPORT_SYMBOL_GPL(snd_soc_add_controls); /** + * snd_soc_add_platform_controls - add an array of controls to a platform. + * Convienience function to add a list of controls. + * + * @platform: platform to add controls to + * @controls: array of controls to add + * @num_controls: number of elements in the array + * + * Return 0 for success, else error. + */ +int snd_soc_add_platform_controls(struct snd_soc_platform *platform, + const struct snd_kcontrol_new *controls, int num_controls) +{ + struct snd_card *card = platform->card->snd_card; + int err, i; + + for (i = 0; i < num_controls; i++) { + const struct snd_kcontrol_new *control = &controls[i]; + err = snd_ctl_add(card, snd_soc_cnew(control, platform, NULL)); + if (err < 0) { + dev_err(platform->dev, "Failed to add %s %d\n", control->name, err); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls); + +/** * snd_soc_info_enum_double - enumerated double mixer info callback * @kcontrol: mixer control * @uinfo: control element information @@ -3162,34 +3312,12 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, { if (dai->driver && dai->driver->ops->set_sysclk) return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); - else if (dai->codec && dai->codec->driver->set_sysclk) - return dai->codec->driver->set_sysclk(dai->codec, clk_id, - freq, dir); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); /** - * snd_soc_codec_set_sysclk - configure CODEC system or master clock. - * @codec: CODEC - * @clk_id: DAI specific clock ID - * @freq: new clock frequency in Hz - * @dir: new clock direction - input/output. - * - * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. - */ -int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, - unsigned int freq, int dir) -{ - if (codec->driver->set_sysclk) - return codec->driver->set_sysclk(codec, clk_id, freq, dir); - else - return -EINVAL; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); - -/** * snd_soc_dai_set_clkdiv - configure DAI clock dividers. * @dai: DAI * @div_id: DAI specific clock divider ID @@ -3225,35 +3353,11 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, if (dai->driver && dai->driver->ops->set_pll) return dai->driver->ops->set_pll(dai, pll_id, source, freq_in, freq_out); - else if (dai->codec && dai->codec->driver->set_pll) - return dai->codec->driver->set_pll(dai->codec, pll_id, source, - freq_in, freq_out); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); -/* - * snd_soc_codec_set_pll - configure codec PLL. - * @codec: CODEC - * @pll_id: DAI specific PLL ID - * @source: DAI specific source for the PLL - * @freq_in: PLL input clock frequency in Hz - * @freq_out: requested PLL output clock frequency in Hz - * - * Configures and enables PLL to generate output clock based on input clock. - */ -int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, - unsigned int freq_in, unsigned int freq_out) -{ - if (codec->driver->set_pll) - return codec->driver->set_pll(codec, pll_id, source, - freq_in, freq_out); - else - return -EINVAL; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); - /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI @@ -3353,20 +3457,17 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); * * @card: Card to register * + * Note that currently this is an internal only function: it will be + * exposed to machine drivers after further backporting of ASoC v2 + * registration APIs. */ -int snd_soc_register_card(struct snd_soc_card *card) +static int snd_soc_register_card(struct snd_soc_card *card) { - int i; + int i, ret = 0; if (!card->name || !card->dev) return -EINVAL; - dev_set_drvdata(card->dev, card); - - snd_soc_initialize_card_lists(card); - - soc_init_card_debugfs(card); - card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * (card->num_links + card->num_aux_devs), GFP_KERNEL); @@ -3374,12 +3475,41 @@ int snd_soc_register_card(struct snd_soc_card *card) return -ENOMEM; card->rtd_aux = &card->rtd[card->num_links]; - for (i = 0; i < card->num_links; i++) + for (i = 0; i < card->num_links; i++) { + /* create virtual CODEC for dynamic links */ + dev_dbg(card->dev, "DAI create runtime %s\n", card->dai_link[i].name); card->rtd[i].dai_link = &card->dai_link[i]; + if (card->rtd[i].dai_link->dynamic) { + + card->rtd[i].dai_link->codec_name = "null-codec"; + card->rtd[i].dai_link->codec_dai_name = "null-codec-dai"; + ret = snd_soc_register_codec(card->dev, &null_codec_drv, + &null_codec_dai_drv, 1); + if (ret < 0) { + printk(KERN_ERR "%s: failed to register dynamic DAI link %d\n", + __func__, ret); + goto out; + } + continue; + } + if (card->rtd[i].dai_link->no_codec) { + card->rtd[i].dai_link->codec_name = "null-codec"; + + ret = snd_soc_register_codec(card->dev, &null_codec_drv, + &null_codec_dai_drv, 1); + if (ret < 0) { + printk(KERN_ERR "%s: failed to register dynamic DAI link %d\n", + __func__, ret); + goto out; + } + continue; + } + } INIT_LIST_HEAD(&card->list); card->instantiated = 0; mutex_init(&card->mutex); + mutex_init(&card->dapm_mutex); mutex_lock(&client_mutex); list_add(&card->list, &card_list); @@ -3387,21 +3517,21 @@ int snd_soc_register_card(struct snd_soc_card *card) mutex_unlock(&client_mutex); dev_dbg(card->dev, "Registered card '%s'\n", card->name); - - return 0; +out: + return ret; } -EXPORT_SYMBOL_GPL(snd_soc_register_card); /** * snd_soc_unregister_card - Unregister a card with the ASoC core * * @card: Card to unregister * + * Note that currently this is an internal only function: it will be + * exposed to machine drivers after further backporting of ASoC v2 + * registration APIs. */ -int snd_soc_unregister_card(struct snd_soc_card *card) +static int snd_soc_unregister_card(struct snd_soc_card *card) { - if (card->instantiated) - soc_cleanup_card_resources(card); mutex_lock(&client_mutex); list_del(&card->list); mutex_unlock(&client_mutex); @@ -3409,7 +3539,6 @@ int snd_soc_unregister_card(struct snd_soc_card *card) return 0; } -EXPORT_SYMBOL_GPL(snd_soc_unregister_card); /* * Simplify DAI link configuration by removing ".-1" from device names @@ -3484,7 +3613,7 @@ int snd_soc_register_dai(struct device *dev, dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); if (dai == NULL) - return -ENOMEM; + return -ENOMEM; /* create DAI component name */ dai->name = fmt_single_name(dev, &dai->id); @@ -3548,6 +3677,7 @@ int snd_soc_register_dais(struct device *dev, int i, ret = 0; dev_dbg(dev, "dai register %s #%Zu\n", dev_name(dev), count); + printk(KERN_INFO "dai register %s #%Zu\n", dev_name(dev), count); for (i = 0; i < count; i++) { @@ -3623,7 +3753,7 @@ int snd_soc_register_platform(struct device *dev, platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL); if (platform == NULL) - return -ENOMEM; + return -ENOMEM; /* create platform component name */ platform->name = fmt_single_name(dev, &platform->id); @@ -3632,6 +3762,10 @@ int snd_soc_register_platform(struct device *dev, return -ENOMEM; } + platform->dapm.bias_level = SND_SOC_BIAS_OFF; + platform->dapm.dev = dev; + platform->dapm.platform = platform; + platform->dapm.stream_event = platform_drv->stream_event; platform->dev = dev; platform->driver = platform_drv; @@ -3726,7 +3860,11 @@ int snd_soc_register_codec(struct device *dev, return -ENOMEM; /* create CODEC component name */ - codec->name = fmt_single_name(dev, &codec->id); + if (codec_drv == &null_codec_drv) + codec->name = kstrdup("null-codec", GFP_KERNEL); + else + codec->name = fmt_single_name(dev, &codec->id); + if (codec->name == NULL) { kfree(codec); return -ENOMEM; @@ -3739,47 +3877,32 @@ int snd_soc_register_codec(struct device *dev, codec->write = codec_drv->write; codec->read = codec_drv->read; - codec->volatile_register = codec_drv->volatile_register; - codec->readable_register = codec_drv->readable_register; - codec->writable_register = codec_drv->writable_register; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; - codec->dapm.seq_notifier = codec_drv->seq_notifier; codec->dev = dev; codec->driver = codec_drv; codec->num_dai = num_dai; + codec->dapm.stream_event = codec_drv->stream_event; mutex_init(&codec->mutex); /* allocate CODEC register cache */ if (codec_drv->reg_cache_size && codec_drv->reg_word_size) { reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - codec->reg_size = reg_size; /* it is necessary to make a copy of the default register cache * because in the case of using a compression type that requires * the default register cache to be marked as __devinitconst the * kernel might have freed the array by the time we initialize * the cache. */ - if (codec_drv->reg_cache_default) { - codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, - reg_size, GFP_KERNEL); - if (!codec->reg_def_copy) { - ret = -ENOMEM; - goto fail; - } + codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, + reg_size, GFP_KERNEL); + if (!codec->reg_def_copy) { + ret = -ENOMEM; + goto fail; } } - if (codec_drv->reg_access_size && codec_drv->reg_access_default) { - if (!codec->volatile_register) - codec->volatile_register = snd_soc_default_volatile_register; - if (!codec->readable_register) - codec->readable_register = snd_soc_default_readable_register; - if (!codec->writable_register) - codec->writable_register = snd_soc_default_writable_register; - } - for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); @@ -3846,38 +3969,34 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS - snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); - if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { + debugfs_root = debugfs_create_dir("asoc", NULL); + if (IS_ERR(debugfs_root) || !debugfs_root) { printk(KERN_WARNING "ASoC: Failed to create debugfs directory\n"); - snd_soc_debugfs_root = NULL; + debugfs_root = NULL; } - if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, + if (!debugfs_create_file("codecs", 0444, debugfs_root, NULL, &codec_list_fops)) pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); - if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, + if (!debugfs_create_file("dais", 0444, debugfs_root, NULL, &dai_list_fops)) pr_warn("ASoC: Failed to create DAI list debugfs file\n"); - if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, + if (!debugfs_create_file("platforms", 0444, debugfs_root, NULL, &platform_list_fops)) pr_warn("ASoC: Failed to create platform list debugfs file\n"); #endif - snd_soc_util_init(); - return platform_driver_register(&soc_driver); } module_init(snd_soc_init); static void __exit snd_soc_exit(void) { - snd_soc_util_exit(); - #ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(snd_soc_debugfs_root); + debugfs_remove_recursive(debugfs_root); #endif platform_driver_unregister(&soc_driver); } -- 1.7.4.1