From 490fba90a90eb7b741f57fefd2bcf2c1e11eb471 Mon Sep 17 00:00:00 2001 From: Michael Nosthoff Date: Fri, 9 Mar 2018 16:13:52 +0100 Subject: [PATCH 001/285] iio: ad7793: implement IIO_CHAN_INFO_SAMP_FREQ This commit is a follow-up to changes made to ad_sigma_delta.h in staging: iio: ad7192: implement IIO_CHAN_INFO_SAMP_FREQ which broke ad7793 as it was not altered to match those changes. This driver predates the availability of IIO_CHAN_INFO_SAMP_FREQ attribute wherein usage has some advantages like it can be accessed by in-kernel consumers as well as reduces the code size. Therefore, use IIO_CHAN_INFO_SAMP_FREQ to implement the sampling_frequency attribute instead of using IIO_DEV_ATTR_SAMP_FREQ() macro. Move code from the functions associated with IIO_DEV_ATTR_SAMP_FREQ() into respective read and write hooks with the mask set to IIO_CHAN_INFO_SAMP_FREQ. Fixes: a13e831fcaa7 ("staging: iio: ad7192: implement IIO_CHAN_INFO_SAMP_FREQ") Signed-off-by: Michael Nosthoff Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 75 +++++++++++++--------------------------- 1 file changed, 24 insertions(+), 51 deletions(-) diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 801afb61310bb1..d4bbe5b5331897 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -348,55 +348,6 @@ static const u16 ad7793_sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, static const u16 ad7797_sample_freq_avail[16] = {0, 0, 0, 123, 62, 50, 0, 33, 0, 17, 16, 12, 10, 8, 6, 4}; -static ssize_t ad7793_read_frequency(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7793_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", - st->chip_info->sample_freq_avail[AD7793_MODE_RATE(st->mode)]); -} - -static ssize_t ad7793_write_frequency(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7793_state *st = iio_priv(indio_dev); - long lval; - int i, ret; - - ret = kstrtol(buf, 10, &lval); - if (ret) - return ret; - - if (lval == 0) - return -EINVAL; - - for (i = 0; i < 16; i++) - if (lval == st->chip_info->sample_freq_avail[i]) - break; - if (i == 16) - return -EINVAL; - - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - st->mode &= ~AD7793_MODE_RATE(-1); - st->mode |= AD7793_MODE_RATE(i); - ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode), st->mode); - iio_device_release_direct_mode(indio_dev); - - return len; -} - -static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, - ad7793_read_frequency, - ad7793_write_frequency); - static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( "470 242 123 62 50 39 33 19 17 16 12 10 8 6 4"); @@ -424,7 +375,6 @@ static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available, ad7793_show_scale_available, NULL, 0); static struct attribute *ad7793_attributes[] = { - &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_const_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_m_in_scale_available.dev_attr.attr, NULL @@ -435,7 +385,6 @@ static const struct attribute_group ad7793_attribute_group = { }; static struct attribute *ad7797_attributes[] = { - &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_const_attr_sampling_frequency_available_ad7797.dev_attr.attr, NULL }; @@ -505,6 +454,10 @@ static int ad7793_read_raw(struct iio_dev *indio_dev, *val -= offset; } return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->chip_info + ->sample_freq_avail[AD7793_MODE_RATE(st->mode)]; + return IIO_VAL_INT; } return -EINVAL; } @@ -542,6 +495,26 @@ static int ad7793_write_raw(struct iio_dev *indio_dev, break; } break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) { + ret = -EINVAL; + break; + } + + for (i = 0; i < 16; i++) + if (val == st->chip_info->sample_freq_avail[i]) + break; + + if (i == 16) { + ret = -EINVAL; + break; + } + + st->mode &= ~AD7793_MODE_RATE(-1); + st->mode |= AD7793_MODE_RATE(i); + ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode), + st->mode); + break; default: ret = -EINVAL; } From 7531cf59bfa082769887ec70c2029838ea139f11 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Tue, 13 Mar 2018 15:23:05 +0100 Subject: [PATCH 002/285] iio: adc: stm32-dfsdm: fix successive oversampling settings When doing successive oversampling settings, it may fail to update filter parameters silently: - First time oversampling is being set, it will be successful, as fl->res is 0 initially. - Next attempts with various oversamp value may return 0 (success), but keep previous filter parameters, due to 'res' never reaches above or equal current 'fl->res'. This is particularly true when setting sampling frequency (that relies on oversamp). Typical failure without error: - run 1st test @16kHz samp freq will succeed - run new test @8kHz will succeed as well - run new test @16kHz (again): sample rate will remain 8kHz without error Fixes: e2e6771c6462 ("IIO: ADC: add STM32 DFSDM sigma delta ADC support") Signed-off-by: Fabrice Gasnier Acked-by: Arnaud Pouliquen Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-adc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 01422d11753cf3..dc911b6fedd675 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -144,6 +144,7 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, * Leave as soon as if exact resolution if reached. * Otherwise the higher resolution below 32 bits is kept. */ + fl->res = 0; for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) { for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) { if (fast) @@ -193,7 +194,7 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, } } - if (!fl->fosr) + if (!fl->res) return -EINVAL; return 0; From d58109dcf37fc9baec354385ec9fdcd8878d174d Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Tue, 13 Mar 2018 15:23:06 +0100 Subject: [PATCH 003/285] iio: adc: stm32-dfsdm: fix sample rate for div2 spi clock When channel clk source is set to "CLKOUT_F" or "CLKOUT_R" (e.g. div2), sample rate is currently set to half the requested value. Fixes: eca949800d2d ("IIO: ADC: add stm32 DFSDM support for PDM microphone") Signed-off-by: Fabrice Gasnier Acked-by: Arnaud Pouliquen Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-adc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index dc911b6fedd675..b28a716a23b279 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -771,7 +771,7 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel]; - unsigned int spi_freq = adc->spi_freq; + unsigned int spi_freq; int ret = -EINVAL; switch (mask) { @@ -785,8 +785,18 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SAMP_FREQ: if (!val) return -EINVAL; - if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) + + switch (ch->src) { + case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL: spi_freq = adc->dfsdm->spi_master_freq; + break; + case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING: + case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING: + spi_freq = adc->dfsdm->spi_master_freq / 2; + break; + default: + spi_freq = adc->spi_freq; + } if (spi_freq % val) dev_warn(&indio_dev->dev, From c043ec1ca5baae63726aae32abbe003192bc6eec Mon Sep 17 00:00:00 2001 From: Martin Kelly Date: Mon, 26 Mar 2018 14:27:51 -0700 Subject: [PATCH 004/285] iio:buffer: make length types match kfifo types Currently, we use int for buffer length and bytes_per_datum. However, kfifo uses unsigned int for length and size_t for element size. We need to make sure these matches or we will have bugs related to overflow (in the range between INT_MAX and UINT_MAX for length, for example). In addition, set_bytes_per_datum uses size_t while bytes_per_datum is an int, which would cause bugs for large values of bytes_per_datum. Change buffer length to use unsigned int and bytes_per_datum to use size_t. Signed-off-by: Martin Kelly Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dma.c | 2 +- drivers/iio/buffer/kfifo_buf.c | 4 ++-- include/linux/iio/buffer_impl.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 05e0c353e08984..b32bf57910caef 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -587,7 +587,7 @@ EXPORT_SYMBOL_GPL(iio_dma_buffer_set_bytes_per_datum); * Should be used as the set_length callback for iio_buffer_access_ops * struct for DMA buffers. */ -int iio_dma_buffer_set_length(struct iio_buffer *buffer, int length) +int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length) { /* Avoid an invalid state */ if (length < 2) diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index 047fe757ab97d6..ac622edf248636 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -22,7 +22,7 @@ struct iio_kfifo { #define iio_to_kfifo(r) container_of(r, struct iio_kfifo, buffer) static inline int __iio_allocate_kfifo(struct iio_kfifo *buf, - int bytes_per_datum, int length) + size_t bytes_per_datum, unsigned int length) { if ((length == 0) || (bytes_per_datum == 0)) return -EINVAL; @@ -67,7 +67,7 @@ static int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd) return 0; } -static int iio_set_length_kfifo(struct iio_buffer *r, int length) +static int iio_set_length_kfifo(struct iio_buffer *r, unsigned int length) { /* Avoid an invalid state */ if (length < 2) diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h index b9e22b7e2f2884..d1171db2374273 100644 --- a/include/linux/iio/buffer_impl.h +++ b/include/linux/iio/buffer_impl.h @@ -53,7 +53,7 @@ struct iio_buffer_access_funcs { int (*request_update)(struct iio_buffer *buffer); int (*set_bytes_per_datum)(struct iio_buffer *buffer, size_t bpd); - int (*set_length)(struct iio_buffer *buffer, int length); + int (*set_length)(struct iio_buffer *buffer, unsigned int length); int (*enable)(struct iio_buffer *buffer, struct iio_dev *indio_dev); int (*disable)(struct iio_buffer *buffer, struct iio_dev *indio_dev); @@ -72,10 +72,10 @@ struct iio_buffer_access_funcs { */ struct iio_buffer { /** @length: Number of datums in buffer. */ - int length; + unsigned int length; /** @bytes_per_datum: Size of individual datum including timestamp. */ - int bytes_per_datum; + size_t bytes_per_datum; /** * @access: Buffer access functions associated with the From 3d13de4b027d5f6276c0f9d3a264f518747d83f2 Mon Sep 17 00:00:00 2001 From: Martin Kelly Date: Mon, 26 Mar 2018 14:27:52 -0700 Subject: [PATCH 005/285] iio:kfifo_buf: check for uint overflow Currently, the following causes a kernel OOPS in memcpy: echo 1073741825 > buffer/length echo 1 > buffer/enable Note that using 1073741824 instead of 1073741825 causes "write error: Cannot allocate memory" but no OOPS. This is because 1073741824 == 2^30 and 1073741825 == 2^30+1. Since kfifo rounds up to the nearest power of 2, it will actually call kmalloc with roundup_pow_of_two(length) * bytes_per_datum. Using length == 1073741825 and bytes_per_datum == 2, we get: kmalloc(roundup_pow_of_two(1073741825) * 2 or kmalloc(2147483648 * 2) or kmalloc(4294967296) or kmalloc(UINT_MAX + 1) so this overflows to 0, causing kmalloc to return ZERO_SIZE_PTR and subsequent memcpy to fail once the device is enabled. Fix this by checking for overflow prior to allocating a kfifo. With this check added, the above code returns -EINVAL when enabling the buffer, rather than causing an OOPS. Signed-off-by: Martin Kelly cc: Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/kfifo_buf.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index ac622edf248636..70c302a93d7fd3 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -27,6 +27,13 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf, if ((length == 0) || (bytes_per_datum == 0)) return -EINVAL; + /* + * Make sure we don't overflow an unsigned int after kfifo rounds up to + * the next power of 2. + */ + if (roundup_pow_of_two(length) > UINT_MAX / bytes_per_datum) + return -EINVAL; + return __kfifo_alloc((struct __kfifo *)&buf->kf, length, bytes_per_datum, GFP_KERNEL); } From f0c8d1f6dc8eac5a1fbf441c8e080721a7b6c0ff Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Tue, 10 Apr 2018 11:57:47 +0300 Subject: [PATCH 006/285] iio: adc: at91-sama5d2_adc: fix channel configuration for differential channels When iterating through the channels, the index in the array is not the scan index. Added an xlate function to translate to the proper index. The result of the bug is that the channel array is indexed with a wrong index, thus instead of the proper channel, we access invalid memory, which may lead to invalid results and/or corruption. This will be used also for devicetree channel xlate. Fixes: 5e1a1da0f ("iio: adc: at91-sama5d2_adc: add hw trigger and buffer support") Fixes: 073c66201 ("iio: adc: at91-sama5d2_adc: add support for DMA") Signed-off-by: Eugen Hristev Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 41 +++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 4eff8351ce2994..8729d6524b4dbd 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -333,6 +333,27 @@ static const struct iio_chan_spec at91_adc_channels[] = { + AT91_SAMA5D2_DIFF_CHAN_CNT + 1), }; +static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].scan_index == chan) + return i; + } + return -EINVAL; +} + +static inline struct iio_chan_spec const * +at91_adc_chan_get(struct iio_dev *indio_dev, int chan) +{ + int index = at91_adc_chan_xlate(indio_dev, chan); + + if (index < 0) + return NULL; + return indio_dev->channels + index; +} + static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) { struct iio_dev *indio = iio_trigger_get_drvdata(trig); @@ -350,8 +371,10 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) at91_adc_writel(st, AT91_SAMA5D2_TRGR, status); for_each_set_bit(bit, indio->active_scan_mask, indio->num_channels) { - struct iio_chan_spec const *chan = indio->channels + bit; + struct iio_chan_spec const *chan = at91_adc_chan_get(indio, bit); + if (!chan) + continue; if (state) { at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel)); @@ -448,7 +471,11 @@ static int at91_adc_dma_start(struct iio_dev *indio_dev) for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->num_channels) { - struct iio_chan_spec const *chan = indio_dev->channels + bit; + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + + if (!chan) + continue; st->dma_st.rx_buf_sz += chan->scan_type.storagebits / 8; } @@ -526,8 +553,11 @@ static int at91_adc_buffer_predisable(struct iio_dev *indio_dev) */ for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->num_channels) { - struct iio_chan_spec const *chan = indio_dev->channels + bit; + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + if (!chan) + continue; if (st->dma_st.dma_chan) at91_adc_readl(st, chan->address); } @@ -587,8 +617,11 @@ static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->num_channels) { - struct iio_chan_spec const *chan = indio_dev->channels + bit; + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + if (!chan) + continue; st->buffer[i] = at91_adc_readl(st, chan->address); i++; } From 6f92253024d9d947a4f454654840ce479e251376 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 14 Apr 2018 17:09:09 +0200 Subject: [PATCH 007/285] iio: hid-sensor-trigger: Fix sometimes not powering up the sensor after resume hid_sensor_set_power_work() powers the sensors back up after a resume based on the user_requested_state atomic_t. But hid_sensor_power_state() treats this as a boolean flag, leading to the following problematic scenario: 1) Some app starts using the iio-sensor in buffered / triggered mode, hid_sensor_data_rdy_trigger_set_state(true) gets called, setting user_requested_state to 1. 2) Something directly accesses a _raw value through sysfs, leading to a call to hid_sensor_power_state(true) followed by hid_sensor_power_state(false) call, this sets user_requested_state to 1 followed by setting it to 0. 3) Suspend/resume the machine, hid_sensor_set_power_work() now does NOT power the sensor back up because user_requested_state (wrongly) is 0. Which stops the app using the sensor in buffered mode from receiving any new values. This commit changes user_requested_state to a counter tracking how many times hid_sensor_power_state(true) was called instead, fixing this. Cc: Bastien Nocera Cc: Srinivas Pandruvada Signed-off-by: Hans de Goede Acked-by: Srinivas Pandruvada Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/common/hid-sensors/hid-sensor-trigger.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index cfb6588565ba2f..4905a997a7ec1f 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -178,14 +178,14 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state) #ifdef CONFIG_PM int ret; - atomic_set(&st->user_requested_state, state); - if (atomic_add_unless(&st->runtime_pm_enable, 1, 1)) pm_runtime_enable(&st->pdev->dev); - if (state) + if (state) { + atomic_inc(&st->user_requested_state); ret = pm_runtime_get_sync(&st->pdev->dev); - else { + } else { + atomic_dec(&st->user_requested_state); pm_runtime_mark_last_busy(&st->pdev->dev); pm_runtime_use_autosuspend(&st->pdev->dev); ret = pm_runtime_put_autosuspend(&st->pdev->dev); From 76974ef9d1bf397b7bb97892a3b3bc516a1fc2c2 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Mon, 16 Apr 2018 09:54:03 +0300 Subject: [PATCH 008/285] iio: adc: select buffer for at91-sama5d2_adc We need to select the buffer code, otherwise we get build errors with undefined functions on the trigger and buffer, if we select just IIO and then AT91_SAMA5D2_ADC from menuconfig This adds a Kconfig 'select' statement like other ADC drivers have it already. Fixes: 5e1a1da0f8c9 ("iio: adc: at91-sama5d2_adc: add hw trigger and buffer support") Signed-off-by: Eugen Hristev Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 72bc2b71765ae2..47bbed3afc8f7d 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -159,6 +159,7 @@ config AT91_SAMA5D2_ADC depends on ARCH_AT91 || COMPILE_TEST depends on HAS_IOMEM depends on HAS_DMA + select IIO_BUFFER select IIO_TRIGGERED_BUFFER help Say yes here to build support for Atmel SAMA5D2 ADC which is From d9f92772e8ec388d070752ee8f187ef8fa18621f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 12 May 2018 02:49:30 -0700 Subject: [PATCH 009/285] xfrm6: avoid potential infinite loop in _decode_session6() syzbot found a way to trigger an infinitie loop by overflowing @offset variable that has been forced to use u16 for some very obscure reason in the past. We probably want to look at NEXTHDR_FRAGMENT handling which looks wrong, in a separate patch. In net-next, we shall try to use skb_header_pointer() instead of pskb_may_pull(). watchdog: BUG: soft lockup - CPU#1 stuck for 134s! [syz-executor738:4553] Modules linked in: irq event stamp: 13885653 hardirqs last enabled at (13885652): [] restore_regs_and_return_to_kernel+0x0/0x2b hardirqs last disabled at (13885653): [] interrupt_entry+0xb5/0xf0 arch/x86/entry/entry_64.S:625 softirqs last enabled at (13614028): [] tun_napi_alloc_frags drivers/net/tun.c:1478 [inline] softirqs last enabled at (13614028): [] tun_get_user+0x1dd9/0x4290 drivers/net/tun.c:1825 softirqs last disabled at (13614032): [] tun_get_user+0x313f/0x4290 drivers/net/tun.c:1942 CPU: 1 PID: 4553 Comm: syz-executor738 Not tainted 4.17.0-rc3+ #40 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:check_kcov_mode kernel/kcov.c:67 [inline] RIP: 0010:__sanitizer_cov_trace_pc+0x20/0x50 kernel/kcov.c:101 RSP: 0018:ffff8801d8cfe250 EFLAGS: 00000246 ORIG_RAX: ffffffffffffff13 RAX: ffff8801d88a8080 RBX: ffff8801d7389e40 RCX: 0000000000000006 RDX: 0000000000000000 RSI: ffffffff868da4ad RDI: ffff8801c8a53277 RBP: ffff8801d8cfe250 R08: ffff8801d88a8080 R09: ffff8801d8cfe3e8 R10: ffffed003b19fc87 R11: ffff8801d8cfe43f R12: ffff8801c8a5327f R13: 0000000000000000 R14: ffff8801c8a4e5fe R15: ffff8801d8cfe3e8 FS: 0000000000d88940(0000) GS:ffff8801daf00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffffffff600400 CR3: 00000001acab3000 CR4: 00000000001406e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: _decode_session6+0xc1d/0x14f0 net/ipv6/xfrm6_policy.c:150 __xfrm_decode_session+0x71/0x140 net/xfrm/xfrm_policy.c:2368 xfrm_decode_session_reverse include/net/xfrm.h:1213 [inline] icmpv6_route_lookup+0x395/0x6e0 net/ipv6/icmp.c:372 icmp6_send+0x1982/0x2da0 net/ipv6/icmp.c:551 icmpv6_send+0x17a/0x300 net/ipv6/ip6_icmp.c:43 ip6_input_finish+0x14e1/0x1a30 net/ipv6/ip6_input.c:305 NF_HOOK include/linux/netfilter.h:288 [inline] ip6_input+0xe1/0x5e0 net/ipv6/ip6_input.c:327 dst_input include/net/dst.h:450 [inline] ip6_rcv_finish+0x29c/0xa10 net/ipv6/ip6_input.c:71 NF_HOOK include/linux/netfilter.h:288 [inline] ipv6_rcv+0xeb8/0x2040 net/ipv6/ip6_input.c:208 __netif_receive_skb_core+0x2468/0x3650 net/core/dev.c:4646 __netif_receive_skb+0x2c/0x1e0 net/core/dev.c:4711 netif_receive_skb_internal+0x126/0x7b0 net/core/dev.c:4785 napi_frags_finish net/core/dev.c:5226 [inline] napi_gro_frags+0x631/0xc40 net/core/dev.c:5299 tun_get_user+0x3168/0x4290 drivers/net/tun.c:1951 tun_chr_write_iter+0xb9/0x154 drivers/net/tun.c:1996 call_write_iter include/linux/fs.h:1784 [inline] do_iter_readv_writev+0x859/0xa50 fs/read_write.c:680 do_iter_write+0x185/0x5f0 fs/read_write.c:959 vfs_writev+0x1c7/0x330 fs/read_write.c:1004 do_writev+0x112/0x2f0 fs/read_write.c:1039 __do_sys_writev fs/read_write.c:1112 [inline] __se_sys_writev fs/read_write.c:1109 [inline] __x64_sys_writev+0x75/0xb0 fs/read_write.c:1109 do_syscall_64+0x1b1/0x800 arch/x86/entry/common.c:287 entry_SYSCALL_64_after_hwframe+0x49/0xbe Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Cc: Steffen Klassert Cc: Nicolas Dichtel Reported-by: syzbot+0053c8...@syzkaller.appspotmail.com Signed-off-by: Steffen Klassert --- net/ipv6/xfrm6_policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 416fe67271a920..86dba282a147ce 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -126,7 +126,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) struct flowi6 *fl6 = &fl->u.ip6; int onlyproto = 0; const struct ipv6hdr *hdr = ipv6_hdr(skb); - u16 offset = sizeof(*hdr); + u32 offset = sizeof(*hdr); struct ipv6_opt_hdr *exthdr; const unsigned char *nh = skb_network_header(skb); u16 nhoff = IP6CB(skb)->nhoff; From dd010bd7af6248b40ab31640a6e31fbebea5aa22 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 15 May 2018 16:04:25 +0300 Subject: [PATCH 010/285] thunderbolt: Handle NULL boot ACL entries properly If the boot ACL entry is already NULL we should not fill in the upper two DWs with 0xfffffffff. Otherwise they are not shown as empty entries when the sysfs attribute is read. Fixes: 9aaa3b8b4c56 ("thunderbolt: Add support for preboot ACL") Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/icm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 2d2ceda9aa2691..500911f1649852 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -1255,7 +1255,7 @@ static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids) /* Map empty entries to null UUID */ uuid[0] = 0; uuid[1] = 0; - } else { + } else if (uuid[0] != 0 || uuid[1] != 0) { /* Upper two DWs are always one's */ uuid[2] = 0xffffffff; uuid[3] = 0xffffffff; From b71534583f22d08c3e3563bf5100aeb5f5c9fbe5 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Wed, 16 May 2018 22:10:37 +0900 Subject: [PATCH 011/285] netfilter: nf_tables: fix NULL pointer dereference on nft_ct_helper_obj_dump() In the nft_ct_helper_obj_dump(), always priv->helper4 is dereferenced. But if family is ipv6, priv->helper6 should be dereferenced. Steps to reproduces: #test.nft table ip6 filter { ct helper ftp { type "ftp" protocol tcp } chain input { type filter hook input priority 4; ct helper set "ftp" } } %nft -f test.nft %nft list ruleset we can see the below messages: [ 916.286233] kasan: GPF could be caused by NULL-ptr deref or user memory access [ 916.294777] general protection fault: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN PTI [ 916.302613] Modules linked in: nft_objref nf_conntrack_sip nf_conntrack_snmp nf_conntrack_broadcast nf_conntrack_ftp nft_ct nf_conntrack nf_tables nfnetlink [last unloaded: nfnetlink] [ 916.318758] CPU: 1 PID: 2093 Comm: nft Not tainted 4.17.0-rc4+ #181 [ 916.326772] Hardware name: To be filled by O.E.M. To be filled by O.E.M./Aptio CRB, BIOS 5.6.5 07/08/2015 [ 916.338773] RIP: 0010:strlen+0x1a/0x90 [ 916.342781] RSP: 0018:ffff88010ff0f2f8 EFLAGS: 00010292 [ 916.346773] RAX: dffffc0000000000 RBX: ffff880119b26ee8 RCX: ffff88010c150038 [ 916.354777] RDX: 0000000000000002 RSI: ffff880119b26ee8 RDI: 0000000000000010 [ 916.362773] RBP: 0000000000000010 R08: 0000000000007e88 R09: ffff88010c15003c [ 916.370773] R10: ffff88010c150037 R11: ffffed002182a007 R12: ffff88010ff04040 [ 916.378779] R13: 0000000000000010 R14: ffff880119b26f30 R15: ffff88010ff04110 [ 916.387265] FS: 00007f57a1997700(0000) GS:ffff88011b800000(0000) knlGS:0000000000000000 [ 916.394785] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 916.402778] CR2: 00007f57a0ac80f0 CR3: 000000010ff02000 CR4: 00000000001006e0 [ 916.410772] Call Trace: [ 916.414787] nft_ct_helper_obj_dump+0x94/0x200 [nft_ct] [ 916.418779] ? nft_ct_set_eval+0x560/0x560 [nft_ct] [ 916.426771] ? memset+0x1f/0x40 [ 916.426771] ? __nla_reserve+0x92/0xb0 [ 916.434774] ? memcpy+0x34/0x50 [ 916.434774] nf_tables_fill_obj_info+0x484/0x860 [nf_tables] [ 916.442773] ? __nft_release_basechain+0x600/0x600 [nf_tables] [ 916.450779] ? lock_acquire+0x193/0x380 [ 916.454771] ? lock_acquire+0x193/0x380 [ 916.458789] ? nf_tables_dump_obj+0x148/0xcb0 [nf_tables] [ 916.462777] nf_tables_dump_obj+0x5f0/0xcb0 [nf_tables] [ 916.470769] ? __alloc_skb+0x30b/0x500 [ 916.474779] netlink_dump+0x752/0xb50 [ 916.478775] __netlink_dump_start+0x4d3/0x750 [ 916.482784] nf_tables_getobj+0x27a/0x930 [nf_tables] [ 916.490774] ? nft_obj_notify+0x100/0x100 [nf_tables] [ 916.494772] ? nf_tables_getobj+0x930/0x930 [nf_tables] [ 916.502579] ? nf_tables_dump_flowtable_done+0x70/0x70 [nf_tables] [ 916.506774] ? nft_obj_notify+0x100/0x100 [nf_tables] [ 916.514808] nfnetlink_rcv_msg+0x8ab/0xa86 [nfnetlink] [ 916.518771] ? nfnetlink_rcv_msg+0x550/0xa86 [nfnetlink] [ 916.526782] netlink_rcv_skb+0x23e/0x360 [ 916.530773] ? nfnetlink_bind+0x200/0x200 [nfnetlink] [ 916.534778] ? debug_check_no_locks_freed+0x280/0x280 [ 916.542770] ? netlink_ack+0x870/0x870 [ 916.546786] ? ns_capable_common+0xf4/0x130 [ 916.550765] nfnetlink_rcv+0x172/0x16c0 [nfnetlink] [ 916.554771] ? sched_clock_local+0xe2/0x150 [ 916.558774] ? sched_clock_cpu+0x144/0x180 [ 916.566575] ? lock_acquire+0x380/0x380 [ 916.570775] ? sched_clock_local+0xe2/0x150 [ 916.574765] ? nfnetlink_net_init+0x130/0x130 [nfnetlink] [ 916.578763] ? sched_clock_cpu+0x144/0x180 [ 916.582770] ? lock_acquire+0x193/0x380 [ 916.590771] ? lock_acquire+0x193/0x380 [ 916.594766] ? lock_acquire+0x380/0x380 [ 916.598760] ? netlink_deliver_tap+0x262/0xa60 [ 916.602766] ? lock_acquire+0x193/0x380 [ 916.606766] netlink_unicast+0x3ef/0x5a0 [ 916.610771] ? netlink_attachskb+0x630/0x630 [ 916.614763] netlink_sendmsg+0x72a/0xb00 [ 916.618769] ? netlink_unicast+0x5a0/0x5a0 [ 916.626766] ? _copy_from_user+0x92/0xc0 [ 916.630773] __sys_sendto+0x202/0x300 [ 916.634772] ? __ia32_sys_getpeername+0xb0/0xb0 [ 916.638759] ? lock_acquire+0x380/0x380 [ 916.642769] ? lock_acquire+0x193/0x380 [ 916.646761] ? finish_task_switch+0xf4/0x560 [ 916.650763] ? __schedule+0x582/0x19a0 [ 916.655301] ? __sched_text_start+0x8/0x8 [ 916.655301] ? up_read+0x1c/0x110 [ 916.655301] ? __do_page_fault+0x48b/0xaa0 [ 916.655301] ? entry_SYSCALL_64_after_hwframe+0x59/0xbe [ 916.655301] __x64_sys_sendto+0xdd/0x1b0 [ 916.655301] do_syscall_64+0x96/0x3d0 [ 916.655301] entry_SYSCALL_64_after_hwframe+0x49/0xbe [ 916.655301] RIP: 0033:0x7f57a0ff5e03 [ 916.655301] RSP: 002b:00007fff6367e0a8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c [ 916.655301] RAX: ffffffffffffffda RBX: 00007fff6367f1e0 RCX: 00007f57a0ff5e03 [ 916.655301] RDX: 0000000000000020 RSI: 00007fff6367e110 RDI: 0000000000000003 [ 916.655301] RBP: 00007fff6367e100 R08: 00007f57a0ce9160 R09: 000000000000000c [ 916.655301] R10: 0000000000000000 R11: 0000000000000246 R12: 00007fff6367e110 [ 916.655301] R13: 0000000000000020 R14: 00007f57a153c610 R15: 0000562417258de0 [ 916.655301] Code: ff ff ff 0f 1f 40 00 66 2e 0f 1f 84 00 00 00 00 00 55 48 89 fa 53 48 c1 ea 03 48 b8 00 00 00 00 00 fc ff df 48 89 fd 48 83 ec 08 <0f> b6 04 02 48 89 fa 83 e2 07 38 d0 7f [ 916.655301] RIP: strlen+0x1a/0x90 RSP: ffff88010ff0f2f8 [ 916.771929] ---[ end trace 1065e048e72479fe ]--- [ 916.777204] Kernel panic - not syncing: Fatal exception [ 916.778158] Kernel Offset: 0x14000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff) Signed-off-by: Taehee Yoo Acked-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_ct.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index ea737fd789e871..5c0de704bad5a1 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -880,22 +880,26 @@ static int nft_ct_helper_obj_dump(struct sk_buff *skb, struct nft_object *obj, bool reset) { const struct nft_ct_helper_obj *priv = nft_obj_data(obj); - const struct nf_conntrack_helper *helper = priv->helper4; + const struct nf_conntrack_helper *helper; u16 family; + if (priv->helper4 && priv->helper6) { + family = NFPROTO_INET; + helper = priv->helper4; + } else if (priv->helper6) { + family = NFPROTO_IPV6; + helper = priv->helper6; + } else { + family = NFPROTO_IPV4; + helper = priv->helper4; + } + if (nla_put_string(skb, NFTA_CT_HELPER_NAME, helper->name)) return -1; if (nla_put_u8(skb, NFTA_CT_HELPER_L4PROTO, priv->l4proto)) return -1; - if (priv->helper4 && priv->helper6) - family = NFPROTO_INET; - else if (priv->helper6) - family = NFPROTO_IPV6; - else - family = NFPROTO_IPV4; - if (nla_put_be16(skb, NFTA_CT_HELPER_L3PROTO, htons(family))) return -1; From 94c752f99954797da583a84c4907ff19e92550a4 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 27 Apr 2018 10:45:31 +0200 Subject: [PATCH 012/285] netfilter: ebtables: handle string from userspace with care strlcpy() can't be safely used on a user-space provided string, as it can try to read beyond the buffer's end, if the latter is not NULL terminated. Leveraging the above, syzbot has been able to trigger the following splat: BUG: KASAN: stack-out-of-bounds in strlcpy include/linux/string.h:300 [inline] BUG: KASAN: stack-out-of-bounds in compat_mtw_from_user net/bridge/netfilter/ebtables.c:1957 [inline] BUG: KASAN: stack-out-of-bounds in ebt_size_mwt net/bridge/netfilter/ebtables.c:2059 [inline] BUG: KASAN: stack-out-of-bounds in size_entry_mwt net/bridge/netfilter/ebtables.c:2155 [inline] BUG: KASAN: stack-out-of-bounds in compat_copy_entries+0x96c/0x14a0 net/bridge/netfilter/ebtables.c:2194 Write of size 33 at addr ffff8801b0abf888 by task syz-executor0/4504 CPU: 0 PID: 4504 Comm: syz-executor0 Not tainted 4.17.0-rc2+ #40 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x1b9/0x294 lib/dump_stack.c:113 print_address_description+0x6c/0x20b mm/kasan/report.c:256 kasan_report_error mm/kasan/report.c:354 [inline] kasan_report.cold.7+0x242/0x2fe mm/kasan/report.c:412 check_memory_region_inline mm/kasan/kasan.c:260 [inline] check_memory_region+0x13e/0x1b0 mm/kasan/kasan.c:267 memcpy+0x37/0x50 mm/kasan/kasan.c:303 strlcpy include/linux/string.h:300 [inline] compat_mtw_from_user net/bridge/netfilter/ebtables.c:1957 [inline] ebt_size_mwt net/bridge/netfilter/ebtables.c:2059 [inline] size_entry_mwt net/bridge/netfilter/ebtables.c:2155 [inline] compat_copy_entries+0x96c/0x14a0 net/bridge/netfilter/ebtables.c:2194 compat_do_replace+0x483/0x900 net/bridge/netfilter/ebtables.c:2285 compat_do_ebt_set_ctl+0x2ac/0x324 net/bridge/netfilter/ebtables.c:2367 compat_nf_sockopt net/netfilter/nf_sockopt.c:144 [inline] compat_nf_setsockopt+0x9b/0x140 net/netfilter/nf_sockopt.c:156 compat_ip_setsockopt+0xff/0x140 net/ipv4/ip_sockglue.c:1279 inet_csk_compat_setsockopt+0x97/0x120 net/ipv4/inet_connection_sock.c:1041 compat_tcp_setsockopt+0x49/0x80 net/ipv4/tcp.c:2901 compat_sock_common_setsockopt+0xb4/0x150 net/core/sock.c:3050 __compat_sys_setsockopt+0x1ab/0x7c0 net/compat.c:403 __do_compat_sys_setsockopt net/compat.c:416 [inline] __se_compat_sys_setsockopt net/compat.c:413 [inline] __ia32_compat_sys_setsockopt+0xbd/0x150 net/compat.c:413 do_syscall_32_irqs_on arch/x86/entry/common.c:323 [inline] do_fast_syscall_32+0x345/0xf9b arch/x86/entry/common.c:394 entry_SYSENTER_compat+0x70/0x7f arch/x86/entry/entry_64_compat.S:139 RIP: 0023:0xf7fb3cb9 RSP: 002b:00000000fff0c26c EFLAGS: 00000282 ORIG_RAX: 000000000000016e RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 0000000000000000 RDX: 0000000000000080 RSI: 0000000020000300 RDI: 00000000000005f4 RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 The buggy address belongs to the page: page:ffffea0006c2afc0 count:0 mapcount:0 mapping:0000000000000000 index:0x0 flags: 0x2fffc0000000000() raw: 02fffc0000000000 0000000000000000 0000000000000000 00000000ffffffff raw: 0000000000000000 ffffea0006c20101 0000000000000000 0000000000000000 page dumped because: kasan: bad access detected Fix the issue replacing the unsafe function with strscpy() and taking care of possible errors. Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support") Reported-and-tested-by: syzbot+4e42a04e0bc33cb6c087@syzkaller.appspotmail.com Signed-off-by: Paolo Abeni Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebtables.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 28a4c3490359cc..6ba639f6c51d12 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1954,7 +1954,8 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt, int off, pad = 0; unsigned int size_kern, match_size = mwt->match_size; - strlcpy(name, mwt->u.name, sizeof(name)); + if (strscpy(name, mwt->u.name, sizeof(name)) < 0) + return -EINVAL; if (state->buf_kern_start) dst = state->buf_kern_start + state->buf_kern_offset; From f3c6a2cfa5d74fb527b95d842e0a949e95845cea Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 16 May 2018 00:00:19 -0700 Subject: [PATCH 013/285] i2c: xlp9xx: Add MAINTAINERS entry The i2c XLP9xx driver is maintained by Cavium. Add George Cherian and Jan Glauber as the Maintainers. Signed-off-by: George Cherian Acked-by: Jan Glauber Signed-off-by: Wolfram Sang --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 078fd80f664fb6..d70f4aeb81ec0d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15513,6 +15513,14 @@ L: linux-kernel@vger.kernel.org S: Supported F: drivers/char/xillybus/ +XLP9XX I2C DRIVER +M: George Cherian +M: Jan Glauber +L: linux-i2c@vger.kernel.org +W: http://www.cavium.com +S: Supported +F: drivers/i2c/busses/i2c-xlp9xx.c + XRA1403 GPIO EXPANDER M: Nandor Han M: Semi Malinen From 97a0549b15a0b466c47f6a0143a490a082c64b4e Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Thu, 17 May 2018 22:49:49 +0900 Subject: [PATCH 014/285] netfilter: nft_meta: fix wrong value dereference in nft_meta_set_eval In the nft_meta_set_eval, nftrace value is dereferenced as u32 from sreg. But correct type is u8. so that sometimes incorrect value is dereferenced. Steps to reproduce: %nft add table ip filter %nft add chain ip filter input { type filter hook input priority 4\; } %nft add rule ip filter input nftrace set 0 %nft monitor Sometimes, we can see trace messages. trace id 16767227 ip filter input packet: iif "enp2s0" ether saddr xx:xx:xx:xx:xx:xx ether daddr xx:xx:xx:xx:xx:xx ip saddr 192.168.0.1 ip daddr 255.255.255.255 ip dscp cs0 ip ecn not-ect ip trace id 16767227 ip filter input rule nftrace set 0 (verdict continue) trace id 16767227 ip filter input verdict continue trace id 16767227 ip filter input Signed-off-by: Taehee Yoo Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 8fb91940e2e724..204af9899482cf 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -234,7 +234,7 @@ void nft_meta_set_eval(const struct nft_expr *expr, struct sk_buff *skb = pkt->skb; u32 *sreg = ®s->data[meta->sreg]; u32 value = *sreg; - u8 pkt_type; + u8 value8; switch (meta->key) { case NFT_META_MARK: @@ -244,15 +244,17 @@ void nft_meta_set_eval(const struct nft_expr *expr, skb->priority = value; break; case NFT_META_PKTTYPE: - pkt_type = nft_reg_load8(sreg); + value8 = nft_reg_load8(sreg); - if (skb->pkt_type != pkt_type && - skb_pkt_type_ok(pkt_type) && + if (skb->pkt_type != value8 && + skb_pkt_type_ok(value8) && skb_pkt_type_ok(skb->pkt_type)) - skb->pkt_type = pkt_type; + skb->pkt_type = value8; break; case NFT_META_NFTRACE: - skb->nf_trace = !!value; + value8 = nft_reg_load8(sreg); + + skb->nf_trace = !!value8; break; default: WARN_ON(1); From 3e0f64b7dd3149f75e8652ff1df56cffeedc8fc1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 16 May 2018 22:58:33 +0200 Subject: [PATCH 015/285] netfilter: nft_limit: fix packet ratelimiting Credit calculations for the packet ratelimiting are not correct, as per the applied ratelimit of 25/second and burst 8, a total of 33 packets should have been accepted. This is true in iptables(33) but not in nftables (~65). For packet ratelimiting, use: div_u64(limit->nsecs, limit->rate) * limit->burst; to calculate credit, just like in iptables' xt_limit does. Moreover, use default burst in iptables, users are expecting similar behaviour. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_limit.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index a9fc298ef4c3a9..72f13a1144dd6a 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c @@ -51,10 +51,13 @@ static inline bool nft_limit_eval(struct nft_limit *limit, u64 cost) return !limit->invert; } +/* Use same default as in iptables. */ +#define NFT_LIMIT_PKT_BURST_DEFAULT 5 + static int nft_limit_init(struct nft_limit *limit, - const struct nlattr * const tb[]) + const struct nlattr * const tb[], bool pkts) { - u64 unit; + u64 unit, tokens; if (tb[NFTA_LIMIT_RATE] == NULL || tb[NFTA_LIMIT_UNIT] == NULL) @@ -68,18 +71,25 @@ static int nft_limit_init(struct nft_limit *limit, if (tb[NFTA_LIMIT_BURST]) limit->burst = ntohl(nla_get_be32(tb[NFTA_LIMIT_BURST])); - else - limit->burst = 0; + + if (pkts && limit->burst == 0) + limit->burst = NFT_LIMIT_PKT_BURST_DEFAULT; if (limit->rate + limit->burst < limit->rate) return -EOVERFLOW; - /* The token bucket size limits the number of tokens can be - * accumulated. tokens_max specifies the bucket size. - * tokens_max = unit * (rate + burst) / rate. - */ - limit->tokens = div_u64(limit->nsecs * (limit->rate + limit->burst), - limit->rate); + if (pkts) { + tokens = div_u64(limit->nsecs, limit->rate) * limit->burst; + } else { + /* The token bucket size limits the number of tokens can be + * accumulated. tokens_max specifies the bucket size. + * tokens_max = unit * (rate + burst) / rate. + */ + tokens = div_u64(limit->nsecs * (limit->rate + limit->burst), + limit->rate); + } + + limit->tokens = tokens; limit->tokens_max = limit->tokens; if (tb[NFTA_LIMIT_FLAGS]) { @@ -144,7 +154,7 @@ static int nft_limit_pkts_init(const struct nft_ctx *ctx, struct nft_limit_pkts *priv = nft_expr_priv(expr); int err; - err = nft_limit_init(&priv->limit, tb); + err = nft_limit_init(&priv->limit, tb, true); if (err < 0) return err; @@ -185,7 +195,7 @@ static int nft_limit_bytes_init(const struct nft_ctx *ctx, { struct nft_limit *priv = nft_expr_priv(expr); - return nft_limit_init(priv, tb); + return nft_limit_init(priv, tb, false); } static int nft_limit_bytes_dump(struct sk_buff *skb, @@ -246,7 +256,7 @@ static int nft_limit_obj_pkts_init(const struct nft_ctx *ctx, struct nft_limit_pkts *priv = nft_obj_data(obj); int err; - err = nft_limit_init(&priv->limit, tb); + err = nft_limit_init(&priv->limit, tb, true); if (err < 0) return err; @@ -289,7 +299,7 @@ static int nft_limit_obj_bytes_init(const struct nft_ctx *ctx, { struct nft_limit *priv = nft_obj_data(obj); - return nft_limit_init(priv, tb); + return nft_limit_init(priv, tb, false); } static int nft_limit_obj_bytes_dump(struct sk_buff *skb, From 52f96757905bbf0edef47f3ee6c7c784e7f8ff8a Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Sat, 19 May 2018 18:22:35 +0300 Subject: [PATCH 016/285] ipvs: fix buffer overflow with sync daemon and service syzkaller reports for buffer overflow for interface name when starting sync daemons [1] What we do is that we copy user structure into larger stack buffer but later we search NUL past the stack buffer. The same happens for sched_name when adding/editing virtual server. We are restricted by IP_VS_SCHEDNAME_MAXLEN and IP_VS_IFNAME_MAXLEN being used as size in include/uapi/linux/ip_vs.h, so they include the space for NUL. As using strlcpy is wrong for unsafe source, replace it with strscpy and add checks to return EINVAL if source string is not NUL-terminated. The incomplete strlcpy fix comes from 2.6.13. For the netlink interface reduce the len parameter for IPVS_DAEMON_ATTR_MCAST_IFN and IPVS_SVC_ATTR_SCHED_NAME, so that we get proper EINVAL. [1] kernel BUG at lib/string.c:1052! invalid opcode: 0000 [#1] SMP KASAN Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: CPU: 1 PID: 373 Comm: syz-executor936 Not tainted 4.17.0-rc4+ #45 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:fortify_panic+0x13/0x20 lib/string.c:1051 RSP: 0018:ffff8801c976f800 EFLAGS: 00010282 RAX: 0000000000000022 RBX: 0000000000000040 RCX: 0000000000000000 RDX: 0000000000000022 RSI: ffffffff8160f6f1 RDI: ffffed00392edef6 RBP: ffff8801c976f800 R08: ffff8801cf4c62c0 R09: ffffed003b5e4fb0 R10: ffffed003b5e4fb0 R11: ffff8801daf27d87 R12: ffff8801c976fa20 R13: ffff8801c976fae4 R14: ffff8801c976fae0 R15: 000000000000048b FS: 00007fd99f75e700(0000) GS:ffff8801daf00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000200001c0 CR3: 00000001d6843000 CR4: 00000000001406e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: strlen include/linux/string.h:270 [inline] strlcpy include/linux/string.h:293 [inline] do_ip_vs_set_ctl+0x31c/0x1d00 net/netfilter/ipvs/ip_vs_ctl.c:2388 nf_sockopt net/netfilter/nf_sockopt.c:106 [inline] nf_setsockopt+0x7d/0xd0 net/netfilter/nf_sockopt.c:115 ip_setsockopt+0xd8/0xf0 net/ipv4/ip_sockglue.c:1253 udp_setsockopt+0x62/0xa0 net/ipv4/udp.c:2487 ipv6_setsockopt+0x149/0x170 net/ipv6/ipv6_sockglue.c:917 tcp_setsockopt+0x93/0xe0 net/ipv4/tcp.c:3057 sock_common_setsockopt+0x9a/0xe0 net/core/sock.c:3046 __sys_setsockopt+0x1bd/0x390 net/socket.c:1903 __do_sys_setsockopt net/socket.c:1914 [inline] __se_sys_setsockopt net/socket.c:1911 [inline] __x64_sys_setsockopt+0xbe/0x150 net/socket.c:1911 do_syscall_64+0x1b1/0x800 arch/x86/entry/common.c:287 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x447369 RSP: 002b:00007fd99f75dda8 EFLAGS: 00000246 ORIG_RAX: 0000000000000036 RAX: ffffffffffffffda RBX: 00000000006e39e4 RCX: 0000000000447369 RDX: 000000000000048b RSI: 0000000000000000 RDI: 0000000000000003 RBP: 0000000000000000 R08: 0000000000000018 R09: 0000000000000000 R10: 00000000200001c0 R11: 0000000000000246 R12: 00000000006e39e0 R13: 75a1ff93f0896195 R14: 6f745f3168746576 R15: 0000000000000001 Code: 08 5b 41 5c 41 5d 41 5e 41 5f 5d c3 0f 0b 48 89 df e8 d2 8f 48 fa eb de 55 48 89 fe 48 c7 c7 60 65 64 88 48 89 e5 e8 91 dd f3 f9 <0f> 0b 90 90 90 90 90 90 90 90 90 90 90 55 48 89 e5 41 57 41 56 RIP: fortify_panic+0x13/0x20 lib/string.c:1051 RSP: ffff8801c976f800 Reported-and-tested-by: syzbot+aac887f77319868646df@syzkaller.appspotmail.com Fixes: e4ff67513096 ("ipvs: add sync_maxlen parameter for the sync daemon") Fixes: 4da62fc70d7c ("[IPVS]: Fix for overflows") Signed-off-by: Julian Anastasov Acked-by: Simon Horman Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_ctl.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index f36098887ad0c4..3ecca0616d8c7d 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2381,8 +2381,10 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) struct ipvs_sync_daemon_cfg cfg; memset(&cfg, 0, sizeof(cfg)); - strlcpy(cfg.mcast_ifn, dm->mcast_ifn, - sizeof(cfg.mcast_ifn)); + ret = -EINVAL; + if (strscpy(cfg.mcast_ifn, dm->mcast_ifn, + sizeof(cfg.mcast_ifn)) <= 0) + goto out_dec; cfg.syncid = dm->syncid; ret = start_sync_thread(ipvs, &cfg, dm->state); } else { @@ -2420,12 +2422,19 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) } } + if ((cmd == IP_VS_SO_SET_ADD || cmd == IP_VS_SO_SET_EDIT) && + strnlen(usvc.sched_name, IP_VS_SCHEDNAME_MAXLEN) == + IP_VS_SCHEDNAME_MAXLEN) { + ret = -EINVAL; + goto out_unlock; + } + /* Check for valid protocol: TCP or UDP or SCTP, even for fwmark!=0 */ if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP && usvc.protocol != IPPROTO_SCTP) { - pr_err("set_ctl: invalid protocol: %d %pI4:%d %s\n", + pr_err("set_ctl: invalid protocol: %d %pI4:%d\n", usvc.protocol, &usvc.addr.ip, - ntohs(usvc.port), usvc.sched_name); + ntohs(usvc.port)); ret = -EFAULT; goto out_unlock; } @@ -2847,7 +2856,7 @@ static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = { static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = { [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 }, [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING, - .len = IP_VS_IFNAME_MAXLEN }, + .len = IP_VS_IFNAME_MAXLEN - 1 }, [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 }, [IPVS_DAEMON_ATTR_SYNC_MAXLEN] = { .type = NLA_U16 }, [IPVS_DAEMON_ATTR_MCAST_GROUP] = { .type = NLA_U32 }, @@ -2865,7 +2874,7 @@ static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = { [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 }, [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 }, [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING, - .len = IP_VS_SCHEDNAME_MAXLEN }, + .len = IP_VS_SCHEDNAME_MAXLEN - 1 }, [IPVS_SVC_ATTR_PE_NAME] = { .type = NLA_NUL_STRING, .len = IP_VS_PENAME_MAXLEN }, [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY, From 4b83a9049a983b20b1ec2757727c5e39f5847ad2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 21 May 2018 16:35:24 -0700 Subject: [PATCH 017/285] netfilter: provide correct argument to nla_strlcpy() Recent patch forgot to remove nla_data(), upsetting syzkaller a bit. BUG: KASAN: slab-out-of-bounds in nla_strlcpy+0x13d/0x150 lib/nlattr.c:314 Read of size 1 at addr ffff8801ad1f4fdd by task syz-executor189/4509 CPU: 1 PID: 4509 Comm: syz-executor189 Not tainted 4.17.0-rc6+ #62 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x1b9/0x294 lib/dump_stack.c:113 print_address_description+0x6c/0x20b mm/kasan/report.c:256 kasan_report_error mm/kasan/report.c:354 [inline] kasan_report.cold.7+0x242/0x2fe mm/kasan/report.c:412 __asan_report_load1_noabort+0x14/0x20 mm/kasan/report.c:430 nla_strlcpy+0x13d/0x150 lib/nlattr.c:314 nfnl_acct_new+0x574/0xc50 net/netfilter/nfnetlink_acct.c:118 nfnetlink_rcv_msg+0xdb5/0xff0 net/netfilter/nfnetlink.c:212 netlink_rcv_skb+0x172/0x440 net/netlink/af_netlink.c:2448 nfnetlink_rcv+0x1fe/0x1ba0 net/netfilter/nfnetlink.c:513 netlink_unicast_kernel net/netlink/af_netlink.c:1310 [inline] netlink_unicast+0x58b/0x740 net/netlink/af_netlink.c:1336 netlink_sendmsg+0x9f0/0xfa0 net/netlink/af_netlink.c:1901 sock_sendmsg_nosec net/socket.c:629 [inline] sock_sendmsg+0xd5/0x120 net/socket.c:639 sock_write_iter+0x35a/0x5a0 net/socket.c:908 call_write_iter include/linux/fs.h:1784 [inline] new_sync_write fs/read_write.c:474 [inline] __vfs_write+0x64d/0x960 fs/read_write.c:487 vfs_write+0x1f8/0x560 fs/read_write.c:549 ksys_write+0xf9/0x250 fs/read_write.c:598 __do_sys_write fs/read_write.c:610 [inline] __se_sys_write fs/read_write.c:607 [inline] __x64_sys_write+0x73/0xb0 fs/read_write.c:607 Fixes: 4e09fc873d92 ("netfilter: prefer nla_strlcpy for dealing with NLA_STRING attributes") Signed-off-by: Eric Dumazet Acked-by: Florian Westphal Reported-by: syzbot Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_acct.c | 2 +- net/netfilter/nfnetlink_cthelper.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index 6ddf89183e7b47..a0e5adf0b3b6dd 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -115,7 +115,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl, nfacct->flags = flags; } - nla_strlcpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); + nla_strlcpy(nfacct->name, tb[NFACCT_NAME], NFACCT_NAME_MAX); if (tb[NFACCT_BYTES]) { atomic64_set(&nfacct->bytes, diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index fa026b269b3691..cb5b5f2077774c 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -150,7 +150,7 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, return -EINVAL; nla_strlcpy(expect_policy->name, - nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN); + tb[NFCTH_POLICY_NAME], NF_CT_HELPER_NAME_LEN); expect_policy->max_expected = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); if (expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) @@ -235,7 +235,7 @@ nfnl_cthelper_create(const struct nlattr * const tb[], goto err1; nla_strlcpy(helper->name, - nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN); + tb[NFCTH_NAME], NF_CT_HELPER_NAME_LEN); size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); if (size > FIELD_SIZEOF(struct nf_conn_help, data)) { ret = -ENOMEM; From 4faa99965e027cc057c5145ce45fa772caa04e8d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 23 May 2018 22:53:22 -0400 Subject: [PATCH 018/285] fix io_destroy()/aio_complete() race If io_destroy() gets to cancelling everything that can be cancelled and gets to kiocb_cancel() calling the function driver has left in ->ki_cancel, it becomes vulnerable to a race with IO completion. At that point req is already taken off the list and aio_complete() does *NOT* spin until we (in free_ioctx_users()) releases ->ctx_lock. As the result, it proceeds to kiocb_free(), freing req just it gets passed to ->ki_cancel(). Fix is simple - remove from the list after the call of kiocb_cancel(). All instances of ->ki_cancel() already have to cope with the being called with iocb still on list - that's what happens in io_cancel(2). Cc: stable@kernel.org Fixes: 0460fef2a921 "aio: use cancellation list lazily" Signed-off-by: Al Viro --- fs/aio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/aio.c b/fs/aio.c index 8061d9787e547d..49f53516eef090 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -634,9 +634,8 @@ static void free_ioctx_users(struct percpu_ref *ref) while (!list_empty(&ctx->active_reqs)) { req = list_first_entry(&ctx->active_reqs, struct aio_kiocb, ki_list); - - list_del_init(&req->ki_list); kiocb_cancel(req); + list_del_init(&req->ki_list); } spin_unlock_irq(&ctx->ctx_lock); From 32795631e67e16141aa5e065c28ba03bf17abb90 Mon Sep 17 00:00:00 2001 From: Mathias Kresin Date: Sun, 8 Apr 2018 10:30:03 +0200 Subject: [PATCH 019/285] MIPS: lantiq: gphy: Drop reboot/remove reset asserts While doing a global software reset, these bits are not cleared and let some bootloader fail to initialise the GPHYs. The bootloader don't expect the GPHYs in reset, as they aren't during power on. The asserts were a workaround for a wrong syscon-reboot mask. With a mask set which includes the GPHY resets, these resets aren't required any more. Fixes: 126534141b45 ("MIPS: lantiq: Add a GPHY driver which uses the RCU syscon-mfd") Signed-off-by: Mathias Kresin Acked-by: Martin Blumenstingl Acked-by: Hauke Mehrtens Cc: John Crispin Cc: linux-mips@linux-mips.org Cc: # 4.14+ Patchwork: https://patchwork.linux-mips.org/patch/19003/ [jhogan@kernel.org: Fix build warnings] Signed-off-by: James Hogan --- drivers/soc/lantiq/gphy.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/drivers/soc/lantiq/gphy.c b/drivers/soc/lantiq/gphy.c index 8d8659463b3e85..feeb17cebc25e9 100644 --- a/drivers/soc/lantiq/gphy.c +++ b/drivers/soc/lantiq/gphy.c @@ -30,7 +30,6 @@ struct xway_gphy_priv { struct clk *gphy_clk_gate; struct reset_control *gphy_reset; struct reset_control *gphy_reset2; - struct notifier_block gphy_reboot_nb; void __iomem *membase; char *fw_name; }; @@ -64,24 +63,6 @@ static const struct of_device_id xway_gphy_match[] = { }; MODULE_DEVICE_TABLE(of, xway_gphy_match); -static struct xway_gphy_priv *to_xway_gphy_priv(struct notifier_block *nb) -{ - return container_of(nb, struct xway_gphy_priv, gphy_reboot_nb); -} - -static int xway_gphy_reboot_notify(struct notifier_block *reboot_nb, - unsigned long code, void *unused) -{ - struct xway_gphy_priv *priv = to_xway_gphy_priv(reboot_nb); - - if (priv) { - reset_control_assert(priv->gphy_reset); - reset_control_assert(priv->gphy_reset2); - } - - return NOTIFY_DONE; -} - static int xway_gphy_load(struct device *dev, struct xway_gphy_priv *priv, dma_addr_t *dev_addr) { @@ -205,14 +186,6 @@ static int xway_gphy_probe(struct platform_device *pdev) reset_control_deassert(priv->gphy_reset); reset_control_deassert(priv->gphy_reset2); - /* assert the gphy reset because it can hang after a reboot: */ - priv->gphy_reboot_nb.notifier_call = xway_gphy_reboot_notify; - priv->gphy_reboot_nb.priority = -1; - - ret = register_reboot_notifier(&priv->gphy_reboot_nb); - if (ret) - dev_warn(dev, "Failed to register reboot notifier\n"); - platform_set_drvdata(pdev, priv); return ret; @@ -220,21 +193,12 @@ static int xway_gphy_probe(struct platform_device *pdev) static int xway_gphy_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct xway_gphy_priv *priv = platform_get_drvdata(pdev); - int ret; - - reset_control_assert(priv->gphy_reset); - reset_control_assert(priv->gphy_reset2); iowrite32be(0, priv->membase); clk_disable_unprepare(priv->gphy_clk_gate); - ret = unregister_reboot_notifier(&priv->gphy_reboot_nb); - if (ret) - dev_warn(dev, "Failed to unregister reboot notifier\n"); - return 0; } From 28e4213dd331e944e7fca1954a946829162ed9d4 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Tue, 15 May 2018 23:04:44 +0100 Subject: [PATCH 020/285] MIPS: prctl: Disallow FRE without FR with PR_SET_FP_MODE requests Having PR_FP_MODE_FRE (i.e. Config5.FRE) set without PR_FP_MODE_FR (i.e. Status.FR) is not supported as the lone purpose of Config5.FRE is to emulate Status.FR=0 handling on FPU hardware that has Status.FR=1 hardwired[1][2]. Also we do not handle this case elsewhere, and assume throughout our code that TIF_HYBRID_FPREGS and TIF_32BIT_FPREGS cannot be set both at once for a task, leading to inconsistent behaviour if this does happen. Return unsuccessfully then from prctl(2) PR_SET_FP_MODE calls requesting PR_FP_MODE_FRE to be set with PR_FP_MODE_FR clear. This corresponds to modes allowed by `mips_set_personality_fp'. References: [1] "MIPS Architecture For Programmers, Vol. III: MIPS32 / microMIPS32 Privileged Resource Architecture", Imagination Technologies, Document Number: MD00090, Revision 6.02, July 10, 2015, Table 9.69 "Config5 Register Field Descriptions", p. 262 [2] "MIPS Architecture For Programmers, Volume III: MIPS64 / microMIPS64 Privileged Resource Architecture", Imagination Technologies, Document Number: MD00091, Revision 6.03, December 22, 2015, Table 9.72 "Config5 Register Field Descriptions", p. 288 Fixes: 9791554b45a2 ("MIPS,prctl: add PR_[GS]ET_FP_MODE prctl options for MIPS") Signed-off-by: Maciej W. Rozycki Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: # 4.0+ Patchwork: https://patchwork.linux-mips.org/patch/19327/ Signed-off-by: James Hogan --- arch/mips/kernel/process.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index b9e9bf6288497b..3775a8d694fb08 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -721,6 +721,10 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value) if (value & ~known_bits) return -EOPNOTSUPP; + /* Setting FRE without FR is not supported. */ + if ((value & (PR_FP_MODE_FR | PR_FP_MODE_FRE)) == PR_FP_MODE_FRE) + return -EOPNOTSUPP; + /* Avoid inadvertently triggering emulation */ if ((value & PR_FP_MODE_FR) && raw_cpu_has_fpu && !(raw_current_cpu_data.fpu_id & MIPS_FPIR_F64)) From c7e814628df65f424fe197dde73bfc67e4a244d7 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 16 May 2018 16:39:58 +0100 Subject: [PATCH 021/285] MIPS: ptrace: Fix PTRACE_PEEKUSR requests for 64-bit FGRs Use 64-bit accesses for 64-bit floating-point general registers with PTRACE_PEEKUSR, removing the truncation of their upper halves in the FR=1 mode, caused by commit bbd426f542cb ("MIPS: Simplify FP context access"), which inadvertently switched them to using 32-bit accesses. The PTRACE_POKEUSR side is fine as it's never been broken and continues using 64-bit accesses. Fixes: bbd426f542cb ("MIPS: Simplify FP context access") Signed-off-by: Maciej W. Rozycki Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: # 3.15+ Patchwork: https://patchwork.linux-mips.org/patch/19334/ Signed-off-by: James Hogan --- arch/mips/kernel/ptrace.c | 2 +- arch/mips/kernel/ptrace32.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 8d098b9f395c13..0c0c23c9c9f5a7 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -818,7 +818,7 @@ long arch_ptrace(struct task_struct *child, long request, break; } #endif - tmp = get_fpr32(&fregs[addr - FPR_BASE], 0); + tmp = get_fpr64(&fregs[addr - FPR_BASE], 0); break; case PC: tmp = regs->cp0_epc; diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index 656a137c1fe2c4..f30c381d3e1ced 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -109,7 +109,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, addr & 1); break; } - tmp = get_fpr32(&fregs[addr - FPR_BASE], 0); + tmp = get_fpr64(&fregs[addr - FPR_BASE], 0); break; case PC: tmp = regs->cp0_epc; From bdcc02cf1bb508fc700df7662f55058f651f2621 Mon Sep 17 00:00:00 2001 From: Dhinakaran Pandiyan Date: Fri, 11 May 2018 12:51:42 -0700 Subject: [PATCH 022/285] drm/psr: Fix missed entry in PSR setup time table. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Entry corresponding to 220 us setup time was missing. I am not aware of any specific bug this fixes, but this could potentially result in enabling PSR on a panel with a higher setup time requirement than supported by the hardware. I verified the value is present in eDP spec versions 1.3, 1.4 and 1.4a. Fixes: 6608804b3d7f ("drm/dp: Add drm_dp_psr_setup_time()") Cc: stable@vger.kernel.org Cc: Ville Syrjälä Cc: Jose Roberto de Souza Cc: dri-devel@lists.freedesktop.org Reviewed-by: José Roberto de Souza Reviewed-by: Tarun Vyas Signed-off-by: Dhinakaran Pandiyan Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20180511195145.3829-3-dhinakaran.pandiyan@intel.com --- drivers/gpu/drm/drm_dp_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index ffe14ec3e7f274..70ae1f23233108 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -1145,6 +1145,7 @@ int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]) static const u16 psr_setup_time_us[] = { PSR_SETUP_TIME(330), PSR_SETUP_TIME(275), + PSR_SETUP_TIME(220), PSR_SETUP_TIME(165), PSR_SETUP_TIME(110), PSR_SETUP_TIME(55), From 2bc5ff0bdc00d81d719dad74589317a260d583ed Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 24 May 2018 10:58:25 +0300 Subject: [PATCH 023/285] drm/omap: fix NULL deref crash with SDI displays Fix a NULL deref bug introduced in commit 24aac6011f70 ("drm: omapdrm: sdi: Allocate the sdi private data structure dynamically"). Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/2f803bfc-3ffe-332a-7b9a-d59a39db4630@ti.com Fixes: 24aac6011f70 ("drm: omapdrm: sdi: Allocate the sdi private data structure dynamically") Reported-by: Tony Lindgren Tested-by: Tony Lindgren Reviewed-by: Benoit Parrot --- drivers/gpu/drm/omapdrm/dss/sdi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index 68a40ae26f5bea..1e2c931f6acfa4 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -82,7 +82,7 @@ static int sdi_calc_clock_div(struct sdi_device *sdi, unsigned long pclk, struct dispc_clock_info *dispc_cinfo) { int i; - struct sdi_clk_calc_ctx ctx = { .sdi = sdi }; + struct sdi_clk_calc_ctx ctx; /* * DSS fclk gives us very few possibilities, so finding a good pixel @@ -95,6 +95,9 @@ static int sdi_calc_clock_div(struct sdi_device *sdi, unsigned long pclk, bool ok; memset(&ctx, 0, sizeof(ctx)); + + ctx.sdi = sdi; + if (pclk > 1000 * i * i * i) ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu); else From b5e2ced9bf81393034072dd4d372f6b430bc1f0a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 24 May 2018 11:27:26 +0300 Subject: [PATCH 024/285] stm class: Use vmalloc for the master map Fengguang is running into a warning from the buddy allocator: > swapper/0: page allocation failure: order:9, mode:0x14040c0(GFP_KERNEL|__GFP_COMP), nodemask=(null) > CPU: 1 PID: 1 Comm: swapper/0 Not tainted 4.17.0-rc1 #262 > Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 > Call Trace: ... > __kmalloc+0x14b/0x180: ____cache_alloc at mm/slab.c:3127 > stm_register_device+0xf3/0x5c0: stm_register_device at drivers/hwtracing/stm/core.c:695 ... Which is basically a result of the stm class trying to allocate ~512kB for the dummy_stm with its default parameters. There's no reason, however, for it not to be vmalloc()ed instead, which is what this patch does. Reported-by: Fengguang Wu Signed-off-by: Alexander Shishkin CC: stable@vger.kernel.org # v4.4+ Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 05386b76465ed5..657badb479a5e9 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -674,7 +674,7 @@ static void stm_device_release(struct device *dev) { struct stm_device *stm = to_stm_device(dev); - kfree(stm); + vfree(stm); } int stm_register_device(struct device *parent, struct stm_data *stm_data, @@ -691,7 +691,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, return -EINVAL; nmasters = stm_data->sw_end - stm_data->sw_start + 1; - stm = kzalloc(sizeof(*stm) + nmasters * sizeof(void *), GFP_KERNEL); + stm = vzalloc(sizeof(*stm) + nmasters * sizeof(void *)); if (!stm) return -ENOMEM; @@ -744,7 +744,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, /* matches device_initialize() above */ put_device(&stm->dev); err_free: - kfree(stm); + vfree(stm); return err; } From 0ed2424b911f3a058dfea01b78817abed767433d Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 24 May 2018 11:27:27 +0300 Subject: [PATCH 025/285] intel_th: Use correct device when freeing buffers Commit d5c435df4a890 ("intel_th: msu: Use the real device in case of IOMMU domain allocation") changes dma buffer allocation to use the actual underlying device, but forgets to change the deallocation path, which leads to (if you've got CAP_SYS_RAWIO): > # echo 0,0 > /sys/bus/intel_th/devices/0-msc0/nr_pages > ------------[ cut here ]------------ > kernel BUG at ../linux/drivers/iommu/intel-iommu.c:3670! > CPU: 3 PID: 231 Comm: sh Not tainted 4.17.0-rc1+ #2729 > RIP: 0010:intel_unmap+0x11e/0x130 ... > Call Trace: > intel_free_coherent+0x3e/0x60 > msc_buffer_win_free+0x100/0x160 [intel_th_msu] This patch fixes the buffer deallocation code to use the correct device. Signed-off-by: Alexander Shishkin Fixes: d5c435df4a890 ("intel_th: msu: Use the real device in case of IOMMU domain allocation") Reported-by: Baofeng Tian CC: stable@vger.kernel.org # v4.14+ Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/msu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index ede388309376f5..634f58042c77f3 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -733,8 +733,8 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) /* Reset the page to write-back before releasing */ set_memory_wb((unsigned long)win->block[i].bdesc, 1); #endif - dma_free_coherent(msc_dev(msc), size, win->block[i].bdesc, - win->block[i].addr); + dma_free_coherent(msc_dev(msc)->parent->parent, size, + win->block[i].bdesc, win->block[i].addr); } kfree(win); @@ -769,7 +769,7 @@ static void msc_buffer_win_free(struct msc *msc, struct msc_window *win) /* Reset the page to write-back before releasing */ set_memory_wb((unsigned long)win->block[i].bdesc, 1); #endif - dma_free_coherent(msc_dev(win->msc), PAGE_SIZE, + dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE, win->block[i].bdesc, win->block[i].addr); } From 6e04b103568983bd699fac96b80a9b96ede68118 Mon Sep 17 00:00:00 2001 From: Devesh Sharma Date: Fri, 25 May 2018 12:01:21 -0400 Subject: [PATCH 026/285] RDMA/bnxt_re: Fix broken RoCE driver due to recent L2 driver changes The recent changes in Broadcom's ethernet driver(L2 driver) broke RoCE functionality in terms of MSIx vector allocation and de-allocation. There is a possibility that L2 driver would initiate MSIx vector reallocation depending upon the requests coming from administrator. In such cases L2 driver needs to free up all the MSIx vectors allocated previously and reallocate/initialize those. If RoCE driver is loaded and reshuffling is attempted, there will be kernel crashes because RoCE driver would still be holding the MSIx vectors but L2 driver would attempt to free in-use vectors. Thus leading to a kernel crash. Making changes in roce driver to fix crashes described above. As part of solution L2 driver tells RoCE driver to release the MSIx vector whenever there is a need. When RoCE driver get message it sync up with all the running tasklets and IRQ handlers and releases the vectors. L2 driver send one more message to RoCE driver to resume the MSIx vectors. L2 driver guarantees that RoCE vector do not change during reshuffling. Fixes: ec86f14ea506 ("bnxt_en: Add ULP calls to stop and restart IRQs.") Fixes: 08654eb213a8 ("bnxt_en: Change IRQ assignment for RDMA driver.") Signed-off-by: Devesh Sharma Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/bnxt_re/main.c | 55 ++++++++++++- drivers/infiniband/hw/bnxt_re/qplib_fp.c | 94 ++++++++++++++-------- drivers/infiniband/hw/bnxt_re/qplib_fp.h | 3 + drivers/infiniband/hw/bnxt_re/qplib_rcfw.c | 61 +++++++++----- drivers/infiniband/hw/bnxt_re/qplib_rcfw.h | 3 + 5 files changed, 163 insertions(+), 53 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index f6c739ec8b620b..20b9f31052bf97 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -185,12 +185,65 @@ static void bnxt_re_shutdown(void *p) bnxt_re_ib_unreg(rdev, false); } +static void bnxt_re_stop_irq(void *handle) +{ + struct bnxt_re_dev *rdev = (struct bnxt_re_dev *)handle; + struct bnxt_qplib_rcfw *rcfw = &rdev->rcfw; + struct bnxt_qplib_nq *nq; + int indx; + + for (indx = BNXT_RE_NQ_IDX; indx < rdev->num_msix; indx++) { + nq = &rdev->nq[indx - 1]; + bnxt_qplib_nq_stop_irq(nq, false); + } + + bnxt_qplib_rcfw_stop_irq(rcfw, false); +} + +static void bnxt_re_start_irq(void *handle, struct bnxt_msix_entry *ent) +{ + struct bnxt_re_dev *rdev = (struct bnxt_re_dev *)handle; + struct bnxt_msix_entry *msix_ent = rdev->msix_entries; + struct bnxt_qplib_rcfw *rcfw = &rdev->rcfw; + struct bnxt_qplib_nq *nq; + int indx, rc; + + if (!ent) { + /* Not setting the f/w timeout bit in rcfw. + * During the driver unload the first command + * to f/w will timeout and that will set the + * timeout bit. + */ + dev_err(rdev_to_dev(rdev), "Failed to re-start IRQs\n"); + return; + } + + /* Vectors may change after restart, so update with new vectors + * in device sctructure. + */ + for (indx = 0; indx < rdev->num_msix; indx++) + rdev->msix_entries[indx].vector = ent[indx].vector; + + bnxt_qplib_rcfw_start_irq(rcfw, msix_ent[BNXT_RE_AEQ_IDX].vector, + false); + for (indx = BNXT_RE_NQ_IDX ; indx < rdev->num_msix; indx++) { + nq = &rdev->nq[indx - 1]; + rc = bnxt_qplib_nq_start_irq(nq, indx - 1, + msix_ent[indx].vector, false); + if (rc) + dev_warn(rdev_to_dev(rdev), + "Failed to reinit NQ index %d\n", indx - 1); + } +} + static struct bnxt_ulp_ops bnxt_re_ulp_ops = { .ulp_async_notifier = NULL, .ulp_stop = bnxt_re_stop, .ulp_start = bnxt_re_start, .ulp_sriov_config = bnxt_re_sriov_config, - .ulp_shutdown = bnxt_re_shutdown + .ulp_shutdown = bnxt_re_shutdown, + .ulp_irq_stop = bnxt_re_stop_irq, + .ulp_irq_restart = bnxt_re_start_irq }; /* RoCE -> Net driver */ diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index 3a78faba8d91b2..50d8f1fc98d5bf 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -336,22 +336,32 @@ static irqreturn_t bnxt_qplib_nq_irq(int irq, void *dev_instance) return IRQ_HANDLED; } +void bnxt_qplib_nq_stop_irq(struct bnxt_qplib_nq *nq, bool kill) +{ + tasklet_disable(&nq->worker); + /* Mask h/w interrupt */ + NQ_DB(nq->bar_reg_iomem, nq->hwq.cons, nq->hwq.max_elements); + /* Sync with last running IRQ handler */ + synchronize_irq(nq->vector); + if (kill) + tasklet_kill(&nq->worker); + if (nq->requested) { + irq_set_affinity_hint(nq->vector, NULL); + free_irq(nq->vector, nq); + nq->requested = false; + } +} + void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq) { if (nq->cqn_wq) { destroy_workqueue(nq->cqn_wq); nq->cqn_wq = NULL; } + /* Make sure the HW is stopped! */ - synchronize_irq(nq->vector); - tasklet_disable(&nq->worker); - tasklet_kill(&nq->worker); + bnxt_qplib_nq_stop_irq(nq, true); - if (nq->requested) { - irq_set_affinity_hint(nq->vector, NULL); - free_irq(nq->vector, nq); - nq->requested = false; - } if (nq->bar_reg_iomem) iounmap(nq->bar_reg_iomem); nq->bar_reg_iomem = NULL; @@ -361,6 +371,40 @@ void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq) nq->vector = 0; } +int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx, + int msix_vector, bool need_init) +{ + int rc; + + if (nq->requested) + return -EFAULT; + + nq->vector = msix_vector; + if (need_init) + tasklet_init(&nq->worker, bnxt_qplib_service_nq, + (unsigned long)nq); + else + tasklet_enable(&nq->worker); + + snprintf(nq->name, sizeof(nq->name), "bnxt_qplib_nq-%d", nq_indx); + rc = request_irq(nq->vector, bnxt_qplib_nq_irq, 0, nq->name, nq); + if (rc) + return rc; + + cpumask_clear(&nq->mask); + cpumask_set_cpu(nq_indx, &nq->mask); + rc = irq_set_affinity_hint(nq->vector, &nq->mask); + if (rc) { + dev_warn(&nq->pdev->dev, + "QPLIB: set affinity failed; vector: %d nq_idx: %d\n", + nq->vector, nq_indx); + } + nq->requested = true; + NQ_DB_REARM(nq->bar_reg_iomem, nq->hwq.cons, nq->hwq.max_elements); + + return rc; +} + int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, int nq_idx, int msix_vector, int bar_reg_offset, int (*cqn_handler)(struct bnxt_qplib_nq *nq, @@ -372,41 +416,17 @@ int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, resource_size_t nq_base; int rc = -1; - nq->pdev = pdev; - nq->vector = msix_vector; if (cqn_handler) nq->cqn_handler = cqn_handler; if (srqn_handler) nq->srqn_handler = srqn_handler; - tasklet_init(&nq->worker, bnxt_qplib_service_nq, (unsigned long)nq); - /* Have a task to schedule CQ notifiers in post send case */ nq->cqn_wq = create_singlethread_workqueue("bnxt_qplib_nq"); if (!nq->cqn_wq) - goto fail; - - nq->requested = false; - memset(nq->name, 0, 32); - sprintf(nq->name, "bnxt_qplib_nq-%d", nq_idx); - rc = request_irq(nq->vector, bnxt_qplib_nq_irq, 0, nq->name, nq); - if (rc) { - dev_err(&nq->pdev->dev, - "Failed to request IRQ for NQ: %#x", rc); - goto fail; - } - - cpumask_clear(&nq->mask); - cpumask_set_cpu(nq_idx, &nq->mask); - rc = irq_set_affinity_hint(nq->vector, &nq->mask); - if (rc) { - dev_warn(&nq->pdev->dev, - "QPLIB: set affinity failed; vector: %d nq_idx: %d\n", - nq->vector, nq_idx); - } + return -ENOMEM; - nq->requested = true; nq->bar_reg = NQ_CONS_PCI_BAR_REGION; nq->bar_reg_off = bar_reg_offset; nq_base = pci_resource_start(pdev, nq->bar_reg); @@ -419,7 +439,13 @@ int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, rc = -ENOMEM; goto fail; } - NQ_DB_REARM(nq->bar_reg_iomem, nq->hwq.cons, nq->hwq.max_elements); + + rc = bnxt_qplib_nq_start_irq(nq, nq_idx, msix_vector, true); + if (rc) { + dev_err(&nq->pdev->dev, + "QPLIB: Failed to request irq for nq-idx %d", nq_idx); + goto fail; + } return 0; fail: diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h index ade9f13c0fd1bb..72352ca80ace70 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h @@ -467,7 +467,10 @@ struct bnxt_qplib_nq_work { struct bnxt_qplib_cq *cq; }; +void bnxt_qplib_nq_stop_irq(struct bnxt_qplib_nq *nq, bool kill); void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq); +int bnxt_qplib_nq_start_irq(struct bnxt_qplib_nq *nq, int nq_indx, + int msix_vector, bool need_init); int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, int nq_idx, int msix_vector, int bar_reg_offset, int (*cqn_handler)(struct bnxt_qplib_nq *nq, diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index 80027a494730df..2852d350ada165 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -582,19 +582,29 @@ int bnxt_qplib_alloc_rcfw_channel(struct pci_dev *pdev, return -ENOMEM; } -void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw) +void bnxt_qplib_rcfw_stop_irq(struct bnxt_qplib_rcfw *rcfw, bool kill) { - unsigned long indx; - - /* Make sure the HW channel is stopped! */ - synchronize_irq(rcfw->vector); tasklet_disable(&rcfw->worker); - tasklet_kill(&rcfw->worker); + /* Mask h/w interrupts */ + CREQ_DB(rcfw->creq_bar_reg_iomem, rcfw->creq.cons, + rcfw->creq.max_elements); + /* Sync with last running IRQ-handler */ + synchronize_irq(rcfw->vector); + if (kill) + tasklet_kill(&rcfw->worker); if (rcfw->requested) { free_irq(rcfw->vector, rcfw); rcfw->requested = false; } +} + +void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw) +{ + unsigned long indx; + + bnxt_qplib_rcfw_stop_irq(rcfw, true); + if (rcfw->cmdq_bar_reg_iomem) iounmap(rcfw->cmdq_bar_reg_iomem); rcfw->cmdq_bar_reg_iomem = NULL; @@ -614,6 +624,31 @@ void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw) rcfw->vector = 0; } +int bnxt_qplib_rcfw_start_irq(struct bnxt_qplib_rcfw *rcfw, int msix_vector, + bool need_init) +{ + int rc; + + if (rcfw->requested) + return -EFAULT; + + rcfw->vector = msix_vector; + if (need_init) + tasklet_init(&rcfw->worker, + bnxt_qplib_service_creq, (unsigned long)rcfw); + else + tasklet_enable(&rcfw->worker); + rc = request_irq(rcfw->vector, bnxt_qplib_creq_irq, 0, + "bnxt_qplib_creq", rcfw); + if (rc) + return rc; + rcfw->requested = true; + CREQ_DB_REARM(rcfw->creq_bar_reg_iomem, rcfw->creq.cons, + rcfw->creq.max_elements); + + return 0; +} + int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev, struct bnxt_qplib_rcfw *rcfw, int msix_vector, @@ -675,27 +710,17 @@ int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev, rcfw->creq_qp_event_processed = 0; rcfw->creq_func_event_processed = 0; - rcfw->vector = msix_vector; if (aeq_handler) rcfw->aeq_handler = aeq_handler; + init_waitqueue_head(&rcfw->waitq); - tasklet_init(&rcfw->worker, bnxt_qplib_service_creq, - (unsigned long)rcfw); - - rcfw->requested = false; - rc = request_irq(rcfw->vector, bnxt_qplib_creq_irq, 0, - "bnxt_qplib_creq", rcfw); + rc = bnxt_qplib_rcfw_start_irq(rcfw, msix_vector, true); if (rc) { dev_err(&rcfw->pdev->dev, "QPLIB: Failed to request IRQ for CREQ rc = 0x%x", rc); bnxt_qplib_disable_rcfw_channel(rcfw); return rc; } - rcfw->requested = true; - - init_waitqueue_head(&rcfw->waitq); - - CREQ_DB_REARM(rcfw->creq_bar_reg_iomem, 0, rcfw->creq.max_elements); init.cmdq_pbl = cpu_to_le64(rcfw->cmdq.pbl[PBL_LVL_0].pg_map_arr[0]); init.cmdq_size_cmdq_lvl = cpu_to_le16( diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h index c7cce2e4185e68..46416dfe8830ea 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h @@ -195,7 +195,10 @@ struct bnxt_qplib_rcfw { void bnxt_qplib_free_rcfw_channel(struct bnxt_qplib_rcfw *rcfw); int bnxt_qplib_alloc_rcfw_channel(struct pci_dev *pdev, struct bnxt_qplib_rcfw *rcfw, int qp_tbl_sz); +void bnxt_qplib_rcfw_stop_irq(struct bnxt_qplib_rcfw *rcfw, bool kill); void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw); +int bnxt_qplib_rcfw_start_irq(struct bnxt_qplib_rcfw *rcfw, int msix_vector, + bool need_init); int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev, struct bnxt_qplib_rcfw *rcfw, int msix_vector, From 806e30873f0e74d9d41b0ef761bd4d3e55c7d510 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 26 May 2018 08:49:24 +0200 Subject: [PATCH 027/285] hwtracing: stm: fix build error on some arches Commit b5e2ced9bf81 ("stm class: Use vmalloc for the master map") caused a build error on some arches as vmalloc.h was not explicitly included. Fix that by adding it to the list of includes. Fixes: b5e2ced9bf81 ("stm class: Use vmalloc for the master map") Reported-by: kbuild test robot Cc: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 657badb479a5e9..10bcb5d73f90ed 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "stm.h" #include From b3fb22733ae61050f8d10a1d6a8af176c5c5db1a Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Fri, 9 Mar 2018 23:22:04 +0100 Subject: [PATCH 028/285] drm/i915: Disable LVDS on Radiant P845 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Radiant P845 does not have LVDS, only VGA. Cc: stable@vger.kernel.org Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=105468 Signed-off-by: Ondrej Zary Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180309222204.4771-1-linux@rainbow-software.org (cherry picked from commit 7f7105f99b75aca4f8c2a748ed6b82c7f8be3293) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/intel_lvds.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 8691c86f579c7d..d0d9988bb6612a 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -827,6 +827,14 @@ static const struct dmi_system_id intel_no_lvds[] = { DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"), }, }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Radiant P845", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Radiant Systems Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "P845"), + }, + }, { } /* terminating entry */ }; From b9eb9c92899a509fe258d38dd6c214b1de69eee0 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 18 May 2018 08:48:40 +0100 Subject: [PATCH 029/285] drm/i915/lvds: Move acpi lid notification registration to registration phase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Delay registering ourselves with the acpi lid notification mechanism until we are registering the connectors after initialisation is complete. This prevents a possibility of trying to handle the lid notification before we are ready with the danger of chasing uninitialised function pointers. BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 IP: (null) PGD 0 P4D 0 Oops: 0010 [#1] PREEMPT SMP PTI Modules linked in: arc4(+) iwldvm(+) i915(+) mac80211 i2c_algo_bit coretemp mei_wdt iwlwifi drm_kms_helper kvm_intel wmi_bmof iTCO_wdt iTCO_vendor_support kvm snd_hda_codec_conexant snd_hda_codec_generic drm psmouse cfg80211 irqbypass input_leds pcspkr i2c_i801 snd_hda_intel snd_hda_codec thinkpad_acpi snd_hda_core mei_me lpc_ich snd_hwdep e1000e wmi nvram snd_pcm mei snd_timer shpchp ptp pps_core rfkill syscopyarea snd intel_agp sysfillrect intel_gtt soundcore sysimgblt battery led_class fb_sys_fops ac rtc_cmos agpgart evdev mac_hid acpi_cpufreq ip_tables x_tables ext4 crc32c_generic crc16 mbcache jbd2 fscrypto crypto_simd glue_helper cryptd aes_x86_64 xts algif_skcipher af_alg dm_crypt dm_mod sd_mod uas usb_storage serio_raw atkbd libps2 ahci libahci uhci_hcd libata scsi_mod ehci_pci ehci_hcd usbcore usb_common i8042 serio CPU: 1 PID: 378 Comm: systemd-logind Not tainted 4.16.8-1-ARCH #1 Hardware name: LENOVO 7454CTO/7454CTO, BIOS 6DET72WW (3.22 ) 10/25/2012 RIP: 0010: (null) RSP: 0018:ffffaf4580c33a18 EFLAGS: 00010287 RAX: 0000000000000000 RBX: ffff947533558000 RCX: 000000000000003e RDX: ffffffffc0aa80c0 RSI: ffffaf4580c33a3c RDI: ffff947534e4c000 RBP: ffff947533558338 R08: ffff947534598930 R09: ffffffffc0a928b1 R10: ffffd8f181d5fd40 R11: 0000000000000000 R12: ffffffffc0a928b1 R13: ffff947533558368 R14: ffffffffc0a928a9 R15: ffff947534e4c000 FS: 00007f3dc4ddb940(0000) GS:ffff947539280000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 000000006e214000 CR4: 00000000000406e0 Call Trace: ? intel_modeset_setup_hw_state+0x385/0xf60 [i915] ? __intel_display_resume+0x1e/0xc0 [i915] ? intel_display_resume+0xcc/0x120 [i915] ? intel_lid_notify+0xbc/0xc0 [i915] ? notifier_call_chain+0x47/0x70 ? blocking_notifier_call_chain+0x3e/0x60 ? acpi_lid_notify_state+0x8f/0x1d0 ? acpi_lid_update_state+0x49/0x70 ? acpi_lid_input_open+0x60/0x90 ? input_open_device+0x5d/0xa0 ? evdev_open+0x1ba/0x1e0 [evdev] ? chrdev_open+0xa3/0x1b0 ? cdev_put.part.0+0x20/0x20 ? do_dentry_open+0x14c/0x300 ? path_openat+0x30c/0x1240 ? current_time+0x16/0x60 ? do_filp_open+0x93/0x100 ? __check_object_size+0xfb/0x180 ? do_sys_open+0x186/0x210 ? do_syscall_64+0x74/0x190 ? entry_SYSCALL_64_after_hwframe+0x3d/0xa2 Code: Bad RIP value. RIP: (null) RSP: ffffaf4580c33a18 CR2: 0000000000000000 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=106559 Fixes: c1c7af608920 ("drm/i915: force mode set at lid open time") Signed-off-by: Chris Wilson Cc: Maarten Lankhorst Cc: Ville Syrjälä Cc: Daniel Vetter Reviewed-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20180518074840.16194-1-chris@chris-wilson.co.uk Cc: stable@vger.kernel.org (cherry picked from commit e578a570dc7c20475774d1ff993825e3bd7a7011) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/intel_lvds.c | 43 +++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index d0d9988bb6612a..e125d16a1aa72d 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -574,6 +574,36 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, return NOTIFY_OK; } +static int +intel_lvds_connector_register(struct drm_connector *connector) +{ + struct intel_lvds_connector *lvds = to_lvds_connector(connector); + int ret; + + ret = intel_connector_register(connector); + if (ret) + return ret; + + lvds->lid_notifier.notifier_call = intel_lid_notify; + if (acpi_lid_notifier_register(&lvds->lid_notifier)) { + DRM_DEBUG_KMS("lid notifier registration failed\n"); + lvds->lid_notifier.notifier_call = NULL; + } + + return 0; +} + +static void +intel_lvds_connector_unregister(struct drm_connector *connector) +{ + struct intel_lvds_connector *lvds = to_lvds_connector(connector); + + if (lvds->lid_notifier.notifier_call) + acpi_lid_notifier_unregister(&lvds->lid_notifier); + + intel_connector_unregister(connector); +} + /** * intel_lvds_destroy - unregister and free LVDS structures * @connector: connector to free @@ -586,9 +616,6 @@ static void intel_lvds_destroy(struct drm_connector *connector) struct intel_lvds_connector *lvds_connector = to_lvds_connector(connector); - if (lvds_connector->lid_notifier.notifier_call) - acpi_lid_notifier_unregister(&lvds_connector->lid_notifier); - if (!IS_ERR_OR_NULL(lvds_connector->base.edid)) kfree(lvds_connector->base.edid); @@ -609,8 +636,8 @@ static const struct drm_connector_funcs intel_lvds_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .atomic_get_property = intel_digital_connector_atomic_get_property, .atomic_set_property = intel_digital_connector_atomic_set_property, - .late_register = intel_connector_register, - .early_unregister = intel_connector_unregister, + .late_register = intel_lvds_connector_register, + .early_unregister = intel_lvds_connector_unregister, .destroy = intel_lvds_destroy, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = intel_digital_connector_duplicate_state, @@ -1158,12 +1185,6 @@ void intel_lvds_init(struct drm_i915_private *dev_priv) lvds_encoder->a3_power = lvds & LVDS_A3_POWER_MASK; - lvds_connector->lid_notifier.notifier_call = intel_lid_notify; - if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) { - DRM_DEBUG_KMS("lid notifier registration failed\n"); - lvds_connector->lid_notifier.notifier_call = NULL; - } - return; failed: From 540ead8c5a0e2910fc7bf0839982921c8f11b31c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 21 May 2018 22:05:30 +0100 Subject: [PATCH 030/285] drm/i915/query: Protect tainted function pointer lookup Smatch identifies i915_query_ioctl() as being a potential victim of Spectre due to its use of a tainted user index into a function pointer array. Use array_index_nospec() to defang the user index before using it to lookup the function pointer. Fixes: a446ae2c6e65 ("drm/i915: add query uAPI") Signed-off-by: Chris Wilson Cc: Lionel Landwerlin Cc: Joonas Lahtinen Cc: Tvrtko Ursulin Reviewed-by: Lionel Landwerlin Link: https://patchwork.freedesktop.org/patch/msgid/20180521210530.26008-1-chris@chris-wilson.co.uk (cherry picked from commit 84b510e22da7926522a257cfe295d3695346a0bd) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/i915_query.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_query.c b/drivers/gpu/drm/i915/i915_query.c index 3ace929dd90ff3..95f9d179afc43e 100644 --- a/drivers/gpu/drm/i915/i915_query.c +++ b/drivers/gpu/drm/i915/i915_query.c @@ -4,6 +4,8 @@ * Copyright © 2018 Intel Corporation */ +#include + #include "i915_drv.h" #include "i915_query.h" #include @@ -111,10 +113,12 @@ int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file) func_idx = item.query_id - 1; - if (func_idx < ARRAY_SIZE(i915_query_funcs)) + ret = -EINVAL; + if (func_idx < ARRAY_SIZE(i915_query_funcs)) { + func_idx = array_index_nospec(func_idx, + ARRAY_SIZE(i915_query_funcs)); ret = i915_query_funcs[func_idx](dev_priv, &item); - else - ret = -EINVAL; + } /* Only write the length back to userspace if they differ. */ if (ret != item.length && put_user(ret, &user_item_ptr->length)) From 533d1daea8d8a389b37207ad7b50c4e750969231 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 May 2018 23:29:59 +0200 Subject: [PATCH 031/285] IB: Revert "remove redundant INFINIBAND kconfig dependencies" Several subsystems depend on INFINIBAND_ADDR_TRANS, which in turn depends on INFINIBAND. However, when with CONFIG_INIFIBAND=m, this leads to a link error when another driver using it is built-in. The INFINIBAND_ADDR_TRANS dependency is insufficient here as this is a 'bool' symbol that does not force anything to be a module in turn. fs/cifs/smbdirect.o: In function `smbd_disconnect_rdma_work': smbdirect.c:(.text+0x1e4): undefined reference to `rdma_disconnect' net/9p/trans_rdma.o: In function `rdma_request': trans_rdma.c:(.text+0x7bc): undefined reference to `rdma_disconnect' net/9p/trans_rdma.o: In function `rdma_destroy_trans': trans_rdma.c:(.text+0x830): undefined reference to `ib_destroy_qp' trans_rdma.c:(.text+0x858): undefined reference to `ib_dealloc_pd' Fixes: 9533b292a7ac ("IB: remove redundant INFINIBAND kconfig dependencies") Signed-off-by: Arnd Bergmann Acked-by: Greg Thelen Signed-off-by: Jason Gunthorpe --- drivers/infiniband/ulp/srpt/Kconfig | 2 +- drivers/nvme/host/Kconfig | 2 +- drivers/nvme/target/Kconfig | 2 +- drivers/staging/lustre/lnet/Kconfig | 2 +- fs/cifs/Kconfig | 2 +- net/9p/Kconfig | 2 +- net/rds/Kconfig | 2 +- net/sunrpc/Kconfig | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/Kconfig b/drivers/infiniband/ulp/srpt/Kconfig index 25bf6955b6d027..fb8b7182f05ebd 100644 --- a/drivers/infiniband/ulp/srpt/Kconfig +++ b/drivers/infiniband/ulp/srpt/Kconfig @@ -1,6 +1,6 @@ config INFINIBAND_SRPT tristate "InfiniBand SCSI RDMA Protocol target support" - depends on INFINIBAND_ADDR_TRANS && TARGET_CORE + depends on INFINIBAND && INFINIBAND_ADDR_TRANS && TARGET_CORE ---help--- Support for the SCSI RDMA Protocol (SRP) Target driver. The diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig index dbb7464c018cac..88a8b5916624ae 100644 --- a/drivers/nvme/host/Kconfig +++ b/drivers/nvme/host/Kconfig @@ -27,7 +27,7 @@ config NVME_FABRICS config NVME_RDMA tristate "NVM Express over Fabrics RDMA host driver" - depends on INFINIBAND_ADDR_TRANS && BLOCK + depends on INFINIBAND && INFINIBAND_ADDR_TRANS && BLOCK select NVME_CORE select NVME_FABRICS select SG_POOL diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig index 7595664ee7531d..3c7b61ddb0d186 100644 --- a/drivers/nvme/target/Kconfig +++ b/drivers/nvme/target/Kconfig @@ -27,7 +27,7 @@ config NVME_TARGET_LOOP config NVME_TARGET_RDMA tristate "NVMe over Fabrics RDMA target support" - depends on INFINIBAND_ADDR_TRANS + depends on INFINIBAND && INFINIBAND_ADDR_TRANS depends on NVME_TARGET select SGL_ALLOC help diff --git a/drivers/staging/lustre/lnet/Kconfig b/drivers/staging/lustre/lnet/Kconfig index f3b1ad4bd3dc77..ad049e6f24e47a 100644 --- a/drivers/staging/lustre/lnet/Kconfig +++ b/drivers/staging/lustre/lnet/Kconfig @@ -34,7 +34,7 @@ config LNET_SELFTEST config LNET_XPRT_IB tristate "LNET infiniband support" - depends on LNET && PCI && INFINIBAND_ADDR_TRANS + depends on LNET && PCI && INFINIBAND && INFINIBAND_ADDR_TRANS default LNET && INFINIBAND help This option allows the LNET users to use infiniband as an diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index d61e2de8d0eb55..5f132d59dfc266 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -197,7 +197,7 @@ config CIFS_SMB311 config CIFS_SMB_DIRECT bool "SMB Direct support (Experimental)" - depends on CIFS=m && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND_ADDR_TRANS=y + depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y help Enables SMB Direct experimental support for SMB 3.0, 3.02 and 3.1.1. SMB Direct allows transferring SMB packets over RDMA. If unsure, diff --git a/net/9p/Kconfig b/net/9p/Kconfig index 46c39f7da444d0..e6014e0e51f7a2 100644 --- a/net/9p/Kconfig +++ b/net/9p/Kconfig @@ -32,7 +32,7 @@ config NET_9P_XEN config NET_9P_RDMA - depends on INET && INFINIBAND_ADDR_TRANS + depends on INET && INFINIBAND && INFINIBAND_ADDR_TRANS tristate "9P RDMA Transport (Experimental)" help This builds support for an RDMA transport. diff --git a/net/rds/Kconfig b/net/rds/Kconfig index 1a31502ee7db30..bffde4b46c5d20 100644 --- a/net/rds/Kconfig +++ b/net/rds/Kconfig @@ -8,7 +8,7 @@ config RDS config RDS_RDMA tristate "RDS over Infiniband" - depends on RDS && INFINIBAND_ADDR_TRANS + depends on RDS && INFINIBAND && INFINIBAND_ADDR_TRANS ---help--- Allow RDS to use Infiniband as a transport. This transport supports RDMA operations. diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig index 6358e52710700d..ac09ca8032965b 100644 --- a/net/sunrpc/Kconfig +++ b/net/sunrpc/Kconfig @@ -50,7 +50,7 @@ config SUNRPC_DEBUG config SUNRPC_XPRT_RDMA tristate "RPC-over-RDMA transport" - depends on SUNRPC && INFINIBAND_ADDR_TRANS + depends on SUNRPC && INFINIBAND && INFINIBAND_ADDR_TRANS default SUNRPC && INFINIBAND select SG_POOL help From ad9d9e85072b668731f356be0a3750a3ba22a607 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 27 May 2018 21:08:13 +0200 Subject: [PATCH 032/285] netfilter: nf_tables: disable preemption in nft_update_chain_stats() This patch fixes the following splat. [118709.054937] BUG: using smp_processor_id() in preemptible [00000000] code: test/1571 [118709.054970] caller is nft_update_chain_stats.isra.4+0x53/0x97 [nf_tables] [118709.054980] CPU: 2 PID: 1571 Comm: test Not tainted 4.17.0-rc6+ #335 [...] [118709.054992] Call Trace: [118709.055011] dump_stack+0x5f/0x86 [118709.055026] check_preemption_disabled+0xd4/0xe4 Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 942702a2776f6a..40e744572283c7 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -126,15 +126,15 @@ static noinline void nft_update_chain_stats(const struct nft_chain *chain, if (!base_chain->stats) return; + local_bh_disable(); stats = this_cpu_ptr(rcu_dereference(base_chain->stats)); if (stats) { - local_bh_disable(); u64_stats_update_begin(&stats->syncp); stats->pkts++; stats->bytes += pkt->skb->len; u64_stats_update_end(&stats->syncp); - local_bh_enable(); } + local_bh_enable(); } struct nft_jumpstack { From 360cc79d9d299ce297b205508276285ceffc5fa8 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Tue, 29 May 2018 01:13:45 +0900 Subject: [PATCH 033/285] netfilter: nf_tables: fix NULL-ptr in nf_tables_dump_obj() The table field in nft_obj_filter is not an array. In order to check tablename, we should check if the pointer is set. Test commands: %nft add table ip filter %nft add counter ip filter ct1 %nft reset counters Splat looks like: [ 306.510504] kasan: CONFIG_KASAN_INLINE enabled [ 306.516184] kasan: GPF could be caused by NULL-ptr deref or user memory access [ 306.524775] general protection fault: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN PTI [ 306.528284] Modules linked in: nft_objref nft_counter nf_tables nfnetlink ip_tables x_tables [ 306.528284] CPU: 0 PID: 1488 Comm: nft Not tainted 4.17.0-rc4+ #17 [ 306.528284] Hardware name: To be filled by O.E.M. To be filled by O.E.M./Aptio CRB, BIOS 5.6.5 07/08/2015 [ 306.528284] RIP: 0010:nf_tables_dump_obj+0x52c/0xa70 [nf_tables] [ 306.528284] RSP: 0018:ffff8800b6cb7520 EFLAGS: 00010246 [ 306.528284] RAX: 0000000000000000 RBX: ffff8800b6c49820 RCX: 0000000000000000 [ 306.528284] RDX: 0000000000000000 RSI: dffffc0000000000 RDI: ffffed0016d96e9a [ 306.528284] RBP: ffff8800b6cb75c0 R08: ffffed00236fce7c R09: ffffed00236fce7b [ 306.528284] R10: ffffffff9f6241e8 R11: ffffed00236fce7c R12: ffff880111365108 [ 306.528284] R13: 0000000000000000 R14: ffff8800b6c49860 R15: ffff8800b6c49860 [ 306.528284] FS: 00007f838b007700(0000) GS:ffff88011b600000(0000) knlGS:0000000000000000 [ 306.528284] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 306.528284] CR2: 00007ffeafabcf78 CR3: 00000000b6cbe000 CR4: 00000000001006f0 [ 306.528284] Call Trace: [ 306.528284] netlink_dump+0x470/0xa20 [ 306.528284] __netlink_dump_start+0x5ae/0x690 [ 306.528284] ? nf_tables_getobj+0x1b3/0x740 [nf_tables] [ 306.528284] nf_tables_getobj+0x2f5/0x740 [nf_tables] [ 306.528284] ? nft_obj_notify+0x100/0x100 [nf_tables] [ 306.528284] ? nf_tables_getobj+0x740/0x740 [nf_tables] [ 306.528284] ? nf_tables_dump_flowtable_done+0x70/0x70 [nf_tables] [ 306.528284] ? nft_obj_notify+0x100/0x100 [nf_tables] [ 306.528284] nfnetlink_rcv_msg+0x8ff/0x932 [nfnetlink] [ 306.528284] ? nfnetlink_rcv_msg+0x216/0x932 [nfnetlink] [ 306.528284] netlink_rcv_skb+0x1c9/0x2f0 [ 306.528284] ? nfnetlink_bind+0x1d0/0x1d0 [nfnetlink] [ 306.528284] ? debug_check_no_locks_freed+0x270/0x270 [ 306.528284] ? netlink_ack+0x7a0/0x7a0 [ 306.528284] ? ns_capable_common+0x6e/0x110 [ ... ] Fixes: e46abbcc05aa8 ("netfilter: nf_tables: Allow table names of up to 255 chars") Signed-off-by: Taehee Yoo Acked-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 91e80aa852d630..2bdc8767aa4010 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4706,7 +4706,7 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) if (idx > s_idx) memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(cb->args[0])); - if (filter && filter->table[0] && + if (filter && filter->table && strcmp(filter->table, table->name)) goto cont; if (filter && @@ -5380,7 +5380,7 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb, if (idx > s_idx) memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(cb->args[0])); - if (filter && filter->table[0] && + if (filter && filter->table && strcmp(filter->table, table->name)) goto cont; From bbb8c61f97e3a2dd91b30d3e57b7964a67569d11 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Tue, 29 May 2018 01:14:12 +0900 Subject: [PATCH 034/285] netfilter: nf_tables: increase nft_counters_enabled in nft_chain_stats_replace() When a chain is updated, a counter can be attached. if so, the nft_counters_enabled should be increased. test commands: %nft add table ip filter %nft add chain ip filter input { type filter hook input priority 4\; } %iptables-compat -Z input %nft delete chain ip filter input we can see below messages. [ 286.443720] jump label: negative count! [ 286.448278] WARNING: CPU: 0 PID: 1459 at kernel/jump_label.c:197 __static_key_slow_dec_cpuslocked+0x6f/0xf0 [ 286.449144] Modules linked in: nf_tables nfnetlink ip_tables x_tables [ 286.449144] CPU: 0 PID: 1459 Comm: nft Tainted: G W 4.17.0-rc2+ #12 [ 286.449144] RIP: 0010:__static_key_slow_dec_cpuslocked+0x6f/0xf0 [ 286.449144] RSP: 0018:ffff88010e5176f0 EFLAGS: 00010286 [ 286.449144] RAX: 000000000000001b RBX: ffffffffc0179500 RCX: ffffffffb8a82522 [ 286.449144] RDX: 0000000000000001 RSI: 0000000000000008 RDI: ffff88011b7e5eac [ 286.449144] RBP: 0000000000000000 R08: ffffed00236fce5c R09: ffffed00236fce5b [ 286.449144] R10: ffffffffc0179503 R11: ffffed00236fce5c R12: 0000000000000000 [ 286.449144] R13: ffff88011a28e448 R14: ffff88011a28e470 R15: dffffc0000000000 [ 286.449144] FS: 00007f0384328700(0000) GS:ffff88011b600000(0000) knlGS:0000000000000000 [ 286.449144] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 286.449144] CR2: 00007f038394bf10 CR3: 0000000104a86000 CR4: 00000000001006f0 [ 286.449144] Call Trace: [ 286.449144] static_key_slow_dec+0x6a/0x70 [ 286.449144] nf_tables_chain_destroy+0x19d/0x210 [nf_tables] [ 286.449144] nf_tables_commit+0x1891/0x1c50 [nf_tables] [ 286.449144] nfnetlink_rcv+0x1148/0x13d0 [nfnetlink] [ ... ] Signed-off-by: Taehee Yoo Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 2bdc8767aa4010..501e48a7965b4a 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1298,8 +1298,10 @@ static void nft_chain_stats_replace(struct nft_base_chain *chain, rcu_assign_pointer(chain->stats, newstats); synchronize_rcu(); free_percpu(oldstats); - } else + } else { rcu_assign_pointer(chain->stats, newstats); + static_branch_inc(&nft_counters_enabled); + } } static void nf_tables_chain_destroy(struct nft_ctx *ctx) From c9ddf73476ff4fffb7a87bd5107a0705bf2cf64b Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 21 May 2018 11:17:29 -0700 Subject: [PATCH 035/285] scsi: scsi_transport_srp: Fix shost to rport translation Since an SRP remote port is attached as a child to shost->shost_gendev and as the only child, the translation from the shost pointer into an rport pointer must happen by looking up the shost child that is an rport. This patch fixes the following KASAN complaint: BUG: KASAN: slab-out-of-bounds in srp_timed_out+0x57/0x110 [scsi_transport_srp] Read of size 4 at addr ffff880035d3fcc0 by task kworker/1:0H/19 CPU: 1 PID: 19 Comm: kworker/1:0H Not tainted 4.16.0-rc3-dbg+ #1 Workqueue: kblockd blk_mq_timeout_work Call Trace: dump_stack+0x85/0xc7 print_address_description+0x65/0x270 kasan_report+0x231/0x350 srp_timed_out+0x57/0x110 [scsi_transport_srp] scsi_times_out+0xc7/0x3f0 [scsi_mod] blk_mq_terminate_expired+0xc2/0x140 bt_iter+0xbc/0xd0 blk_mq_queue_tag_busy_iter+0x1c7/0x350 blk_mq_timeout_work+0x325/0x3f0 process_one_work+0x441/0xa50 worker_thread+0x76/0x6c0 kthread+0x1b2/0x1d0 ret_from_fork+0x24/0x30 Fixes: e68ca75200fe ("scsi_transport_srp: Reduce failover time") Signed-off-by: Bart Van Assche Cc: Hannes Reinecke Cc: Johannes Thumshirn Cc: Jason Gunthorpe Cc: Doug Ledford Cc: Laurence Oberman Cc: stable@vger.kernel.org Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_srp.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c index 36f6190931bc07..456ce9f19569f3 100644 --- a/drivers/scsi/scsi_transport_srp.c +++ b/drivers/scsi/scsi_transport_srp.c @@ -51,6 +51,8 @@ struct srp_internal { struct transport_container rport_attr_cont; }; +static int scsi_is_srp_rport(const struct device *dev); + #define to_srp_internal(tmpl) container_of(tmpl, struct srp_internal, t) #define dev_to_rport(d) container_of(d, struct srp_rport, dev) @@ -60,9 +62,24 @@ static inline struct Scsi_Host *rport_to_shost(struct srp_rport *r) return dev_to_shost(r->dev.parent); } +static int find_child_rport(struct device *dev, void *data) +{ + struct device **child = data; + + if (scsi_is_srp_rport(dev)) { + WARN_ON_ONCE(*child); + *child = dev; + } + return 0; +} + static inline struct srp_rport *shost_to_rport(struct Scsi_Host *shost) { - return transport_class_to_srp_rport(&shost->shost_gendev); + struct device *child = NULL; + + WARN_ON_ONCE(device_for_each_child(&shost->shost_gendev, &child, + find_child_rport) < 0); + return child ? dev_to_rport(child) : NULL; } /** @@ -600,7 +617,8 @@ enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd) struct srp_rport *rport = shost_to_rport(shost); pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev)); - return rport->fast_io_fail_tmo < 0 && rport->dev_loss_tmo < 0 && + return rport && rport->fast_io_fail_tmo < 0 && + rport->dev_loss_tmo < 0 && i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED; } From bbb40a0b75209734ff9286f3326171638c9f6569 Mon Sep 17 00:00:00 2001 From: Mathieu Xhonneux Date: Fri, 25 May 2018 13:29:41 +0100 Subject: [PATCH 036/285] ipv6: sr: fix memory OOB access in seg6_do_srh_encap/inline seg6_do_srh_encap and seg6_do_srh_inline can possibly do an out-of-bounds access when adding the SRH to the packet. This no longer happen when expanding the skb not only by the size of the SRH (+ outer IPv6 header), but also by skb->mac_len. [ 53.793056] BUG: KASAN: use-after-free in seg6_do_srh_encap+0x284/0x620 [ 53.794564] Write of size 14 at addr ffff88011975ecfa by task ping/674 [ 53.796665] CPU: 0 PID: 674 Comm: ping Not tainted 4.17.0-rc3-ARCH+ #90 [ 53.796670] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.11.0-20171110_100015-anatol 04/01/2014 [ 53.796673] Call Trace: [ 53.796679] [ 53.796689] dump_stack+0x71/0xab [ 53.796700] print_address_description+0x6a/0x270 [ 53.796707] kasan_report+0x258/0x380 [ 53.796715] ? seg6_do_srh_encap+0x284/0x620 [ 53.796722] memmove+0x34/0x50 [ 53.796730] seg6_do_srh_encap+0x284/0x620 [ 53.796741] ? seg6_do_srh+0x29b/0x360 [ 53.796747] seg6_do_srh+0x29b/0x360 [ 53.796756] seg6_input+0x2e/0x2e0 [ 53.796765] lwtunnel_input+0x93/0xd0 [ 53.796774] ipv6_rcv+0x690/0x920 [ 53.796783] ? ip6_input+0x170/0x170 [ 53.796791] ? eth_gro_receive+0x2d0/0x2d0 [ 53.796800] ? ip6_input+0x170/0x170 [ 53.796809] __netif_receive_skb_core+0xcc0/0x13f0 [ 53.796820] ? netdev_info+0x110/0x110 [ 53.796827] ? napi_complete_done+0xb6/0x170 [ 53.796834] ? e1000_clean+0x6da/0xf70 [ 53.796845] ? process_backlog+0x129/0x2a0 [ 53.796853] process_backlog+0x129/0x2a0 [ 53.796862] net_rx_action+0x211/0x5c0 [ 53.796870] ? napi_complete_done+0x170/0x170 [ 53.796887] ? run_rebalance_domains+0x11f/0x150 [ 53.796891] __do_softirq+0x10e/0x39e [ 53.796894] do_softirq_own_stack+0x2a/0x40 [ 53.796895] [ 53.796898] do_softirq.part.16+0x54/0x60 [ 53.796900] __local_bh_enable_ip+0x5b/0x60 [ 53.796903] ip6_finish_output2+0x416/0x9f0 [ 53.796906] ? ip6_dst_lookup_flow+0x110/0x110 [ 53.796909] ? ip6_sk_dst_lookup_flow+0x390/0x390 [ 53.796911] ? __rcu_read_unlock+0x66/0x80 [ 53.796913] ? ip6_mtu+0x44/0xf0 [ 53.796916] ? ip6_output+0xfc/0x220 [ 53.796918] ip6_output+0xfc/0x220 [ 53.796921] ? ip6_finish_output+0x2b0/0x2b0 [ 53.796923] ? memcpy+0x34/0x50 [ 53.796926] ip6_send_skb+0x43/0xc0 [ 53.796929] rawv6_sendmsg+0x1216/0x1530 [ 53.796932] ? __orc_find+0x6b/0xc0 [ 53.796934] ? rawv6_rcv_skb+0x160/0x160 [ 53.796937] ? __rcu_read_unlock+0x66/0x80 [ 53.796939] ? __rcu_read_unlock+0x66/0x80 [ 53.796942] ? is_bpf_text_address+0x1e/0x30 [ 53.796944] ? kernel_text_address+0xec/0x100 [ 53.796946] ? __kernel_text_address+0xe/0x30 [ 53.796948] ? unwind_get_return_address+0x2f/0x50 [ 53.796950] ? __save_stack_trace+0x92/0x100 [ 53.796954] ? save_stack+0x89/0xb0 [ 53.796956] ? kasan_kmalloc+0xa0/0xd0 [ 53.796958] ? kmem_cache_alloc+0xd2/0x1f0 [ 53.796961] ? prepare_creds+0x23/0x160 [ 53.796963] ? __x64_sys_capset+0x252/0x3e0 [ 53.796966] ? do_syscall_64+0x69/0x160 [ 53.796968] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 53.796971] ? __alloc_pages_nodemask+0x170/0x380 [ 53.796973] ? __alloc_pages_slowpath+0x12c0/0x12c0 [ 53.796977] ? tty_vhangup+0x20/0x20 [ 53.796979] ? policy_nodemask+0x1a/0x90 [ 53.796982] ? __mod_node_page_state+0x8d/0xa0 [ 53.796986] ? __check_object_size+0xe7/0x240 [ 53.796989] ? __sys_sendto+0x229/0x290 [ 53.796991] ? rawv6_rcv_skb+0x160/0x160 [ 53.796993] __sys_sendto+0x229/0x290 [ 53.796996] ? __ia32_sys_getpeername+0x50/0x50 [ 53.796999] ? commit_creds+0x2de/0x520 [ 53.797002] ? security_capset+0x57/0x70 [ 53.797004] ? __x64_sys_capset+0x29f/0x3e0 [ 53.797007] ? __x64_sys_rt_sigsuspend+0xe0/0xe0 [ 53.797011] ? __do_page_fault+0x664/0x770 [ 53.797014] __x64_sys_sendto+0x74/0x90 [ 53.797017] do_syscall_64+0x69/0x160 [ 53.797019] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 53.797022] RIP: 0033:0x7f43b7a6714a [ 53.797023] RSP: 002b:00007ffd891bd368 EFLAGS: 00000246 ORIG_RAX: 000000000000002c [ 53.797026] RAX: ffffffffffffffda RBX: 00000000006129c0 RCX: 00007f43b7a6714a [ 53.797028] RDX: 0000000000000040 RSI: 00000000006129c0 RDI: 0000000000000004 [ 53.797029] RBP: 00007ffd891be640 R08: 0000000000610940 R09: 000000000000001c [ 53.797030] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000040 [ 53.797032] R13: 000000000060e6a0 R14: 0000000000008004 R15: 000000000040b661 [ 53.797171] Allocated by task 642: [ 53.797460] kasan_kmalloc+0xa0/0xd0 [ 53.797463] kmem_cache_alloc+0xd2/0x1f0 [ 53.797465] getname_flags+0x40/0x210 [ 53.797467] user_path_at_empty+0x1d/0x40 [ 53.797469] do_faccessat+0x12a/0x320 [ 53.797471] do_syscall_64+0x69/0x160 [ 53.797473] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 53.797607] Freed by task 642: [ 53.797869] __kasan_slab_free+0x130/0x180 [ 53.797871] kmem_cache_free+0xa8/0x230 [ 53.797872] filename_lookup+0x15b/0x230 [ 53.797874] do_faccessat+0x12a/0x320 [ 53.797876] do_syscall_64+0x69/0x160 [ 53.797878] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 53.798014] The buggy address belongs to the object at ffff88011975e600 which belongs to the cache names_cache of size 4096 [ 53.799043] The buggy address is located 1786 bytes inside of 4096-byte region [ffff88011975e600, ffff88011975f600) [ 53.800013] The buggy address belongs to the page: [ 53.800414] page:ffffea000465d600 count:1 mapcount:0 mapping:0000000000000000 index:0x0 compound_mapcount: 0 [ 53.801259] flags: 0x17fff0000008100(slab|head) [ 53.801640] raw: 017fff0000008100 0000000000000000 0000000000000000 0000000100070007 [ 53.803147] raw: dead000000000100 dead000000000200 ffff88011b185a40 0000000000000000 [ 53.803787] page dumped because: kasan: bad access detected [ 53.804384] Memory state around the buggy address: [ 53.804788] ffff88011975eb80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 53.805384] ffff88011975ec00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 53.805979] >ffff88011975ec80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 53.806577] ^ [ 53.807165] ffff88011975ed00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 53.807762] ffff88011975ed80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 53.808356] ================================================================== [ 53.808949] Disabling lock debugging due to kernel taint Fixes: 6c8702c60b88 ("ipv6: sr: add support for SRH encapsulation and injection with lwtunnels") Signed-off-by: David Lebrun Signed-off-by: Mathieu Xhonneux Signed-off-by: David S. Miller --- net/ipv6/seg6_iptunnel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 5fe13948491968..bf4763fd68c223 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -103,7 +103,7 @@ int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto) hdrlen = (osrh->hdrlen + 1) << 3; tot_len = hdrlen + sizeof(*hdr); - err = skb_cow_head(skb, tot_len); + err = skb_cow_head(skb, tot_len + skb->mac_len); if (unlikely(err)) return err; @@ -161,7 +161,7 @@ int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) hdrlen = (osrh->hdrlen + 1) << 3; - err = skb_cow_head(skb, hdrlen); + err = skb_cow_head(skb, hdrlen + skb->mac_len); if (unlikely(err)) return err; From 312564269535892cc082bc80592150cd1f5e8ec3 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 25 May 2018 14:50:37 +0200 Subject: [PATCH 037/285] net: netsec: reduce DMA mask to 40 bits The netsec network controller IP can drive 64 address bits for DMA, and the DMA mask is set accordingly in the driver. However, the SynQuacer SoC, which is the only silicon incorporating this IP at the moment, integrates this IP in a manner that leaves address bits [63:40] unconnected. Up until now, this has not resulted in any problems, given that the DDR controller doesn't decode those bits to begin with. However, recent firmware updates for platforms incorporating this SoC allow the IOMMU to be enabled, which does decode address bits [47:40], and allocates top down from the IOVA space, producing DMA addresses that have bits set that have been left unconnected. Both the DT and ACPI (IORT) descriptions of the platform take this into account, and only describe a DMA address space of 40 bits (using either dma-ranges DT properties, or DMA address limits in IORT named component nodes). However, even though our IOMMU and bus layers may take such limitations into account by setting a narrower DMA mask when creating the platform device, the netsec probe() entrypoint follows the common practice of setting the DMA mask uncondionally, according to the capabilities of the IP block itself rather than to its integration into the chip. It is currently unclear what the correct fix is here. We could hack around it by only setting the DMA mask if it deviates from its default value of DMA_BIT_MASK(32). However, this makes it impossible for the bus layer to use DMA_BIT_MASK(32) as the bus limit, and so it appears that a more comprehensive approach is required to take DMA limits imposed by the SoC as a whole into account. In the mean time, let's limit the DMA mask to 40 bits. Given that there is currently only one SoC that incorporates this IP, this is a reasonable approach that can be backported to -stable and buys us some time to come up with a proper fix going forward. Fixes: 533dd11a12f6 ("net: socionext: Add Synquacer NetSec driver") Cc: Robin Murphy Cc: Jassi Brar Cc: Masahisa Kojima Cc: Ilias Apalodimas Signed-off-by: Ard Biesheuvel Reviewed-by: Robin Murphy Acked-by: Jassi Brar Signed-off-by: David S. Miller --- drivers/net/ethernet/socionext/netsec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index f4c0b02ddad82a..59fbf74dcadaf2 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -1674,8 +1674,8 @@ static int netsec_probe(struct platform_device *pdev) if (ret) goto unreg_napi; - if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) - dev_warn(&pdev->dev, "Failed to enable 64-bit DMA\n"); + if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40))) + dev_warn(&pdev->dev, "Failed to set DMA mask\n"); ret = register_netdev(ndev); if (ret) { From 52a192362932f333a7ebafd581c4d9b81da2fec8 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 28 May 2018 13:25:06 +0200 Subject: [PATCH 038/285] Revert "rt2800: use TXOP_BACKOFF for probe frames" This reverts commit fb47ada8dc3c30c8e7b415da155742b49536c61e. In some situations when we set TXOP_BACKOFF, the probe frame is not sent at all. What it worse then sending probe frame as part of AMPDU and can degrade 11n performance to 11g rates. Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00queue.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c index a6884e73d2abfb..7ddee980048bcc 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c @@ -372,16 +372,15 @@ static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, /* * Determine IFS values - * - Use TXOP_BACKOFF for probe and management frames except beacons + * - Use TXOP_BACKOFF for management frames except beacons * - Use TXOP_SIFS for fragment bursts * - Use TXOP_HTTXOP for everything else * * Note: rt2800 devices won't use CTS protection (if used) * for frames not transmitted with TXOP_HTTXOP */ - if ((ieee80211_is_mgmt(hdr->frame_control) && - !ieee80211_is_beacon(hdr->frame_control)) || - (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) + if (ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_beacon(hdr->frame_control)) txdesc->u.ht.txop = TXOP_BACKOFF; else if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) txdesc->u.ht.txop = TXOP_SIFS; From ab1068d6866e28bf6427ceaea681a381e5870a4a Mon Sep 17 00:00:00 2001 From: Hao Wei Tee Date: Tue, 29 May 2018 10:25:17 +0300 Subject: [PATCH 039/285] iwlwifi: pcie: compare with number of IRQs requested for, not number of CPUs When there are 16 or more logical CPUs, we request for `IWL_MAX_RX_HW_QUEUES` (16) IRQs only as we limit to that number of IRQs, but later on we compare the number of IRQs returned to nr_online_cpus+2 instead of max_irqs, the latter being what we actually asked for. This ends up setting num_rx_queues to 17 which causes lots of out-of-bounds array accesses later on. Compare to max_irqs instead, and also add an assertion in case num_rx_queues > IWM_MAX_RX_HW_QUEUES. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=199551 Fixes: 2e5d4a8f61dc ("iwlwifi: pcie: Add new configuration to enable MSIX") Signed-off-by: Hao Wei Tee Tested-by: Sara Sharon Signed-off-by: Luca Coelho Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index f8a0234d332c2a..5517ea4c2aa0b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1590,14 +1590,13 @@ static void iwl_pcie_set_interrupt_capa(struct pci_dev *pdev, struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int max_irqs, num_irqs, i, ret, nr_online_cpus; + int max_irqs, num_irqs, i, ret; u16 pci_cmd; if (!trans->cfg->mq_rx_supported) goto enable_msi; - nr_online_cpus = num_online_cpus(); - max_irqs = min_t(u32, nr_online_cpus + 2, IWL_MAX_RX_HW_QUEUES); + max_irqs = min_t(u32, num_online_cpus() + 2, IWL_MAX_RX_HW_QUEUES); for (i = 0; i < max_irqs; i++) trans_pcie->msix_entries[i].entry = i; @@ -1623,16 +1622,17 @@ static void iwl_pcie_set_interrupt_capa(struct pci_dev *pdev, * Two interrupts less: non rx causes shared with FBQ and RSS. * More than two interrupts: we will use fewer RSS queues. */ - if (num_irqs <= nr_online_cpus) { + if (num_irqs <= max_irqs - 2) { trans_pcie->trans->num_rx_queues = num_irqs + 1; trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX | IWL_SHARED_IRQ_FIRST_RSS; - } else if (num_irqs == nr_online_cpus + 1) { + } else if (num_irqs == max_irqs - 1) { trans_pcie->trans->num_rx_queues = num_irqs; trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX; } else { trans_pcie->trans->num_rx_queues = num_irqs - 1; } + WARN_ON(trans_pcie->trans->num_rx_queues > IWL_MAX_RX_HW_QUEUES); trans_pcie->alloc_vecs = num_irqs; trans_pcie->msix_enabled = true; From 65b3bdc807ac7bd83f5b27bc2c29a3c631eed7dd Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 22 May 2018 13:10:18 +0100 Subject: [PATCH 040/285] drm/i915/query: nospec expects no more than an unsigned long nospec quite reasonably asserts that it will never be used with an index larger than unsigned long (that being the largest possibly index into an C array). However, our ubi uses the convention of u64 for any large integer, running afoul of the assertion on 32b. Reduce our index to an unsigned long, checking for type overflow first. drivers/gpu/drm/i915/i915_query.c: In function 'i915_query_ioctl': include/linux/compiler.h:339:38: error: call to '__compiletime_assert_119' declared with attribute error: BUILD_BUG_ON failed: sizeof(_s) > sizeof(long) Reported-by: kbuild-all@01.org Fixes: 84b510e22da7 ("drm/i915/query: Protect tainted function pointer lookup") Signed-off-by: Chris Wilson Cc: Lionel Landwerlin Cc: Joonas Lahtinen Cc: Tvrtko Ursulin Reviewed-by: Lionel Landwerlin Link: https://patchwork.freedesktop.org/patch/msgid/20180522121018.15199-1-chris@chris-wilson.co.uk (cherry picked from commit a33b1dc8a732144e11cb4bf067d24ba51e6b8ab0) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/i915_query.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_query.c b/drivers/gpu/drm/i915/i915_query.c index 95f9d179afc43e..3f502eef243166 100644 --- a/drivers/gpu/drm/i915/i915_query.c +++ b/drivers/gpu/drm/i915/i915_query.c @@ -102,7 +102,7 @@ int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file) for (i = 0; i < args->num_items; i++, user_item_ptr++) { struct drm_i915_query_item item; - u64 func_idx; + unsigned long func_idx; int ret; if (copy_from_user(&item, user_item_ptr, sizeof(item))) @@ -111,6 +111,9 @@ int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file) if (item.query_id == 0) return -EINVAL; + if (overflows_type(item.query_id - 1, unsigned long)) + return -EINVAL; + func_idx = item.query_id - 1; ret = -EINVAL; From f9c6442a8f0b1dde9e755eb4ff6fa22bcce4eabc Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Fri, 25 May 2018 20:49:52 +0300 Subject: [PATCH 041/285] atm: zatm: fix memcmp casting memcmp() returns int, but eprom_try_esi() cast it to unsigned char. One can lose significant bits and get 0 from non-0 value returned by the memcmp(). Signed-off-by: Ivan Bornyakov Signed-off-by: David S. Miller --- drivers/atm/zatm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c index 9c9a229587176d..a8d2eb0ceb8d8f 100644 --- a/drivers/atm/zatm.c +++ b/drivers/atm/zatm.c @@ -1151,8 +1151,8 @@ static void eprom_get_byte(struct zatm_dev *zatm_dev, unsigned char *byte, } -static unsigned char eprom_try_esi(struct atm_dev *dev, unsigned short cmd, - int offset, int swap) +static int eprom_try_esi(struct atm_dev *dev, unsigned short cmd, int offset, + int swap) { unsigned char buf[ZEPROM_SIZE]; struct zatm_dev *zatm_dev; From 47bf9df2e8201d07c40670e093629f8dfd1b5d9f Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sun, 27 May 2018 09:48:41 +0300 Subject: [PATCH 042/285] mlxsw: spectrum: Forbid creation of VLAN 1 over port/LAG VLAN 1 is internally used for untagged traffic. Prevent creation of explicit netdevice for that VLAN, because that currently isn't supported and leads to the NULL pointer dereference cited below. Fix by preventing creation of VLAN devices with VID of 1 over mlxsw devices or LAG devices that involve mlxsw devices. [ 327.175816] ================================================================================ [ 327.184544] UBSAN: Undefined behaviour in drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c:200:12 [ 327.193667] member access within null pointer of type 'const struct mlxsw_sp_fid' [ 327.201226] CPU: 0 PID: 8983 Comm: ip Not tainted 4.17.0-rc4-petrm_net_ip6gre_headroom-custom-140 #11 [ 327.210496] Hardware name: Mellanox Technologies Ltd. "MSN2410-CB2F"/"SA000874", BIOS 4.6.5 03/08/2016 [ 327.219872] Call Trace: [ 327.222384] dump_stack+0xc3/0x12b [ 327.234007] ubsan_epilogue+0x9/0x49 [ 327.237638] ubsan_type_mismatch_common+0x1f9/0x2d0 [ 327.255769] __ubsan_handle_type_mismatch+0x90/0xa7 [ 327.264716] mlxsw_sp_fid_type+0x35/0x50 [mlxsw_spectrum] [ 327.270255] mlxsw_sp_port_vlan_router_leave+0x46/0xc0 [mlxsw_spectrum] [ 327.277019] mlxsw_sp_inetaddr_port_vlan_event+0xe1/0x340 [mlxsw_spectrum] [ 327.315031] mlxsw_sp_netdevice_vrf_event+0xa8/0x100 [mlxsw_spectrum] [ 327.321626] mlxsw_sp_netdevice_event+0x276/0x430 [mlxsw_spectrum] [ 327.367863] notifier_call_chain+0x4c/0x150 [ 327.372128] __netdev_upper_dev_link+0x1b3/0x260 [ 327.399450] vrf_add_slave+0xce/0x170 [vrf] [ 327.403703] do_setlink+0x658/0x1d70 [ 327.508998] rtnl_newlink+0x908/0xf20 [ 327.559128] rtnetlink_rcv_msg+0x50c/0x720 [ 327.571720] netlink_rcv_skb+0x16a/0x1f0 [ 327.583450] netlink_unicast+0x2ca/0x3e0 [ 327.599305] netlink_sendmsg+0x3e2/0x7f0 [ 327.616655] sock_sendmsg+0x76/0xc0 [ 327.620207] ___sys_sendmsg+0x494/0x5d0 [ 327.666117] __sys_sendmsg+0xc2/0x130 [ 327.690953] do_syscall_64+0x66/0x370 [ 327.694677] entry_SYSCALL_64_after_hwframe+0x49/0xbe [ 327.699782] RIP: 0033:0x7f4c2f3f8037 [ 327.703393] RSP: 002b:00007ffe8c389708 EFLAGS: 00000246 ORIG_RAX: 000000000000002e [ 327.711035] RAX: ffffffffffffffda RBX: 000000005b03f53e RCX: 00007f4c2f3f8037 [ 327.718229] RDX: 0000000000000000 RSI: 00007ffe8c389760 RDI: 0000000000000003 [ 327.725431] RBP: 00007ffe8c389760 R08: 0000000000000000 R09: 00007f4c2f443630 [ 327.732632] R10: 00000000000005eb R11: 0000000000000246 R12: 0000000000000000 [ 327.739833] R13: 00000000006774e0 R14: 00007ffe8c3897e8 R15: 0000000000000000 [ 327.747096] ================================================================================ Fixes: 9589a7b5d7d9 ("mlxsw: spectrum: Handle VLAN devices linking / unlinking") Suggested-by: Ido Schimmel Signed-off-by: Petr Machata Signed-off-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index ca38a30fbe913c..adc6ab2cf42922 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4433,6 +4433,11 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev, NL_SET_ERR_MSG_MOD(extack, "Can not put a VLAN on an OVS port"); return -EINVAL; } + if (is_vlan_dev(upper_dev) && + vlan_dev_vlan_id(upper_dev) == 1) { + NL_SET_ERR_MSG_MOD(extack, "Creating a VLAN device with VID 1 is unsupported: VLAN 1 carries untagged traffic"); + return -EINVAL; + } break; case NETDEV_CHANGEUPPER: upper_dev = info->upper_dev; From 2415f3bd059fe050eb98aedf93664d000ceb4e92 Mon Sep 17 00:00:00 2001 From: Josh Hill Date: Sun, 27 May 2018 20:10:41 -0400 Subject: [PATCH 043/285] net: qmi_wwan: Add Netgear Aircard 779S MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Netgear Aircard 779S Signed-off-by: Josh Hill Acked-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 42565dd33aa66a..094680871687d4 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1103,6 +1103,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x05c6, 0x920d, 5)}, {QMI_QUIRK_SET_DTR(0x05c6, 0x9625, 4)}, /* YUGA CLM920-NC5 */ {QMI_FIXED_INTF(0x0846, 0x68a2, 8)}, + {QMI_FIXED_INTF(0x0846, 0x68d3, 8)}, /* Netgear Aircard 779S */ {QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */ {QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */ {QMI_FIXED_INTF(0x1435, 0xd181, 3)}, /* Wistron NeWeb D18Q1 */ From d2c2725c2cdbcc108a191f50953d31c7b6556761 Mon Sep 17 00:00:00 2001 From: Suresh Reddy Date: Mon, 28 May 2018 01:26:06 -0400 Subject: [PATCH 044/285] be2net: Fix error detection logic for BE3 Check for 0xE00 (RECOVERABLE_ERR) along with ARMFW UE (0x0) in be_detect_error() to know whether the error is valid error or not Fixes: 673c96e5a ("be2net: Fix UE detection logic for BE3") Signed-off-by: Suresh Reddy Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index c697e79e491e32..8f755009ff3820 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -3309,7 +3309,9 @@ void be_detect_error(struct be_adapter *adapter) if ((val & POST_STAGE_FAT_LOG_START) != POST_STAGE_FAT_LOG_START && (val & POST_STAGE_ARMFW_UE) - != POST_STAGE_ARMFW_UE) + != POST_STAGE_ARMFW_UE && + (val & POST_STAGE_RECOVERABLE_ERR) + != POST_STAGE_RECOVERABLE_ERR) return; } From 6547e387d7f52f2ba681a229de3c13e5b9e01ee1 Mon Sep 17 00:00:00 2001 From: Toshiaki Makita Date: Mon, 28 May 2018 19:37:49 +0900 Subject: [PATCH 045/285] tun: Fix NULL pointer dereference in XDP redirect Calling XDP redirection requires bh disabled. Softirq can call another XDP function and redirection functions, then the percpu static variable ri->map can be overwritten to NULL. This is a generic XDP case called from tun. [ 3535.736058] BUG: unable to handle kernel NULL pointer dereference at 0000000000000018 [ 3535.743974] PGD 0 P4D 0 [ 3535.746530] Oops: 0000 [#1] SMP PTI [ 3535.750049] Modules linked in: vhost_net vhost tap tun bridge stp llc ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter sunrpc vfat fat ext4 mbcache jbd2 intel_rapl skx_edac nfit libnvdimm x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm ipmi_ssif irqbypass crct10dif_pclmul crc32_pclmul ghash_clmulni_intel pcbc ses aesni_intel crypto_simd cryptd enclosure hpwdt hpilo glue_helper ipmi_si pcspkr wmi mei_me ioatdma mei ipmi_devintf shpchp dca ipmi_msghandler lpc_ich acpi_power_meter sch_fq_codel ip_tables xfs libcrc32c sd_mod mgag200 i2c_algo_bit drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops ttm drm smartpqi i40e crc32c_intel scsi_transport_sas tg3 i2c_core ptp pps_core [ 3535.813456] CPU: 5 PID: 1630 Comm: vhost-1614 Not tainted 4.17.0-rc4 #2 [ 3535.820127] Hardware name: HPE ProLiant DL360 Gen10/ProLiant DL360 Gen10, BIOS U32 11/14/2017 [ 3535.828732] RIP: 0010:__xdp_map_lookup_elem+0x5/0x30 [ 3535.833740] RSP: 0018:ffffb4bc47bf7c58 EFLAGS: 00010246 [ 3535.839009] RAX: ffff9fdfcfea1c40 RBX: 0000000000000000 RCX: ffff9fdf27fe3100 [ 3535.846205] RDX: ffff9fdfca769200 RSI: 0000000000000000 RDI: 0000000000000000 [ 3535.853402] RBP: ffffb4bc491d9000 R08: 00000000000045ad R09: 0000000000000ec0 [ 3535.860597] R10: 0000000000000001 R11: ffff9fdf26c3ce4e R12: ffff9fdf9e72c000 [ 3535.867794] R13: 0000000000000000 R14: fffffffffffffff2 R15: ffff9fdfc82cdd00 [ 3535.874990] FS: 0000000000000000(0000) GS:ffff9fdfcfe80000(0000) knlGS:0000000000000000 [ 3535.883152] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 3535.888948] CR2: 0000000000000018 CR3: 0000000bde724004 CR4: 00000000007626e0 [ 3535.896145] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 3535.903342] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 3535.910538] PKRU: 55555554 [ 3535.913267] Call Trace: [ 3535.915736] xdp_do_generic_redirect+0x7a/0x310 [ 3535.920310] do_xdp_generic.part.117+0x285/0x370 [ 3535.924970] tun_get_user+0x5b9/0x1260 [tun] [ 3535.929279] tun_sendmsg+0x52/0x70 [tun] [ 3535.933237] handle_tx+0x2ad/0x5f0 [vhost_net] [ 3535.937721] vhost_worker+0xa5/0x100 [vhost] [ 3535.942030] kthread+0xf5/0x130 [ 3535.945198] ? vhost_dev_ioctl+0x3b0/0x3b0 [vhost] [ 3535.950031] ? kthread_bind+0x10/0x10 [ 3535.953727] ret_from_fork+0x35/0x40 [ 3535.957334] Code: 0e 74 15 83 f8 10 75 05 e9 49 aa b3 ff f3 c3 0f 1f 80 00 00 00 00 f3 c3 e9 29 9d b3 ff 66 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 <8b> 47 18 83 f8 0e 74 0d 83 f8 10 75 05 e9 49 a9 b3 ff 31 c0 c3 [ 3535.976387] RIP: __xdp_map_lookup_elem+0x5/0x30 RSP: ffffb4bc47bf7c58 [ 3535.982883] CR2: 0000000000000018 [ 3535.987096] ---[ end trace 383b299dd1430240 ]--- [ 3536.131325] Kernel panic - not syncing: Fatal exception [ 3536.137484] Kernel Offset: 0x26a00000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff) [ 3536.281406] ---[ end Kernel panic - not syncing: Fatal exception ]--- And a kernel with generic case fixed still panics in tun driver XDP redirect, because it disabled only preemption, but not bh. [ 2055.128746] BUG: unable to handle kernel NULL pointer dereference at 0000000000000018 [ 2055.136662] PGD 0 P4D 0 [ 2055.139219] Oops: 0000 [#1] SMP PTI [ 2055.142736] Modules linked in: vhost_net vhost tap tun bridge stp llc ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter sunrpc vfat fat ext4 mbcache jbd2 intel_rapl skx_edac nfit libnvdimm x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm irqbypass crct10dif_pclmul crc32_pclmul ghash_clmulni_intel pcbc ses aesni_intel ipmi_ssif crypto_simd enclosure cryptd hpwdt glue_helper ioatdma hpilo wmi dca pcspkr ipmi_si acpi_power_meter ipmi_devintf shpchp mei_me ipmi_msghandler mei lpc_ich sch_fq_codel ip_tables xfs libcrc32c sd_mod mgag200 i2c_algo_bit drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops ttm drm i40e smartpqi tg3 scsi_transport_sas crc32c_intel i2c_core ptp pps_core [ 2055.206142] CPU: 6 PID: 1693 Comm: vhost-1683 Tainted: G W 4.17.0-rc5-fix-tun+ #1 [ 2055.215011] Hardware name: HPE ProLiant DL360 Gen10/ProLiant DL360 Gen10, BIOS U32 11/14/2017 [ 2055.223617] RIP: 0010:__xdp_map_lookup_elem+0x5/0x30 [ 2055.228624] RSP: 0018:ffff998b07607cc0 EFLAGS: 00010246 [ 2055.233892] RAX: ffff8dbd8e235700 RBX: ffff8dbd8ff21c40 RCX: 0000000000000004 [ 2055.241089] RDX: ffff998b097a9000 RSI: 0000000000000000 RDI: 0000000000000000 [ 2055.248286] RBP: 0000000000000000 R08: 00000000000065a8 R09: 0000000000005d80 [ 2055.255483] R10: 0000000000000040 R11: ffff8dbcf0100000 R12: ffff998b097a9000 [ 2055.262681] R13: ffff8dbd8c98c000 R14: 0000000000000000 R15: ffff998b07607d78 [ 2055.269879] FS: 0000000000000000(0000) GS:ffff8dbd8ff00000(0000) knlGS:0000000000000000 [ 2055.278039] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 2055.283834] CR2: 0000000000000018 CR3: 0000000c0c8cc005 CR4: 00000000007626e0 [ 2055.291030] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 2055.298227] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 2055.305424] PKRU: 55555554 [ 2055.308153] Call Trace: [ 2055.310624] xdp_do_redirect+0x7b/0x380 [ 2055.314499] tun_get_user+0x10fe/0x12a0 [tun] [ 2055.318895] tun_sendmsg+0x52/0x70 [tun] [ 2055.322852] handle_tx+0x2ad/0x5f0 [vhost_net] [ 2055.327337] vhost_worker+0xa5/0x100 [vhost] [ 2055.331646] kthread+0xf5/0x130 [ 2055.334813] ? vhost_dev_ioctl+0x3b0/0x3b0 [vhost] [ 2055.339646] ? kthread_bind+0x10/0x10 [ 2055.343343] ret_from_fork+0x35/0x40 [ 2055.346950] Code: 0e 74 15 83 f8 10 75 05 e9 e9 aa b3 ff f3 c3 0f 1f 80 00 00 00 00 f3 c3 e9 c9 9d b3 ff 66 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 <8b> 47 18 83 f8 0e 74 0d 83 f8 10 75 05 e9 e9 a9 b3 ff 31 c0 c3 [ 2055.366004] RIP: __xdp_map_lookup_elem+0x5/0x30 RSP: ffff998b07607cc0 [ 2055.372500] CR2: 0000000000000018 [ 2055.375856] ---[ end trace 2a2dcc5e9e174268 ]--- [ 2055.523626] Kernel panic - not syncing: Fatal exception [ 2055.529796] Kernel Offset: 0x2e000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff) [ 2055.677539] ---[ end Kernel panic - not syncing: Fatal exception ]--- v2: - Removed preempt_disable/enable since local_bh_disable will prevent preemption as well, feedback from Jason Wang. Fixes: 761876c857cb ("tap: XDP support") Signed-off-by: Toshiaki Makita Acked-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/tun.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 45d807796a18a1..23e9eb66197fbe 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1650,7 +1650,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, else *skb_xdp = 0; - preempt_disable(); + local_bh_disable(); rcu_read_lock(); xdp_prog = rcu_dereference(tun->xdp_prog); if (xdp_prog && !*skb_xdp) { @@ -1675,7 +1675,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, if (err) goto err_redirect; rcu_read_unlock(); - preempt_enable(); + local_bh_enable(); return NULL; case XDP_TX: get_page(alloc_frag->page); @@ -1684,7 +1684,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, goto err_redirect; tun_xdp_flush(tun->dev); rcu_read_unlock(); - preempt_enable(); + local_bh_enable(); return NULL; case XDP_PASS: delta = orig_data - xdp.data; @@ -1703,7 +1703,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, skb = build_skb(buf, buflen); if (!skb) { rcu_read_unlock(); - preempt_enable(); + local_bh_enable(); return ERR_PTR(-ENOMEM); } @@ -1713,7 +1713,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, alloc_frag->offset += buflen; rcu_read_unlock(); - preempt_enable(); + local_bh_enable(); return skb; @@ -1721,7 +1721,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, put_page(alloc_frag->page); err_xdp: rcu_read_unlock(); - preempt_enable(); + local_bh_enable(); this_cpu_inc(tun->pcpu_stats->rx_dropped); return NULL; } @@ -1917,16 +1917,19 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, struct bpf_prog *xdp_prog; int ret; + local_bh_disable(); rcu_read_lock(); xdp_prog = rcu_dereference(tun->xdp_prog); if (xdp_prog) { ret = do_xdp_generic(xdp_prog, skb); if (ret != XDP_PASS) { rcu_read_unlock(); + local_bh_enable(); return total_len; } } rcu_read_unlock(); + local_bh_enable(); } rcu_read_lock(); From a840c93ca7582bb6c88df2345a33f979b7a67874 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Sun, 27 May 2018 14:49:16 +0300 Subject: [PATCH 046/285] IB/core: Fix error code for invalid GID entry When a GID entry is invalid EAGAIN is returned. This is an incorrect error code, there is nothing that will make this GID entry valid again in bounded time. Some user space tools fail incorrectly if EAGAIN is returned here, and this represents a small ABI change from earlier kernels. The first patch in the Fixes list makes entries that were valid before to become invalid, allowing this code to trigger, while the second patch in the Fixes list introduced the wrong EAGAIN. Therefore revert the return result to EINVAL which matches the historical expectations of the ibv_query_gid_type() API of the libibverbs user space library. Cc: Fixes: 598ff6bae689 ("IB/core: Refactor GID modify code for RoCE") Fixes: 03db3a2d81e6 ("IB/core: Add RoCE GID table management") Reviewed-by: Daniel Jurgens Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index fb2d347f760f14..ecc55e98ddd381 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -502,7 +502,7 @@ static int __ib_cache_gid_get(struct ib_device *ib_dev, u8 port, int index, return -EINVAL; if (table->data_vec[index].props & GID_TABLE_ENTRY_INVALID) - return -EAGAIN; + return -EINVAL; memcpy(gid, &table->data_vec[index].gid, sizeof(*gid)); if (attr) { From a0ccb6b54bdde16885ac99f49ee8031cf1c71abe Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Mon, 28 May 2018 10:59:14 +0200 Subject: [PATCH 047/285] i2c: ocores: update HDL sources URL The URL is broken. This patch fixes it. Signed-off-by: Federico Vaga [wsa: shortened the URL a bit] Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-ocores | 2 +- drivers/i2c/busses/i2c-ocores.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/i2c/busses/i2c-ocores b/Documentation/i2c/busses/i2c-ocores index c269aaa2f26a1a..9e1dfe7553ad8e 100644 --- a/Documentation/i2c/busses/i2c-ocores +++ b/Documentation/i2c/busses/i2c-ocores @@ -2,7 +2,7 @@ Kernel driver i2c-ocores Supported adapters: * OpenCores.org I2C controller by Richard Herveille (see datasheet link) - Datasheet: http://www.opencores.org/projects.cgi/web/i2c/overview + https://opencores.org/project/i2c/overview Author: Peter Korsgaard diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 8c42ca7107b2c9..45ae3c025bf680 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -1,6 +1,6 @@ /* * i2c-ocores.c: I2C bus driver for OpenCores I2C controller - * (http://www.opencores.org/projects.cgi/web/i2c/overview). + * (https://opencores.org/project/i2c/overview) * * Peter Korsgaard * From 20fa2ff0441eabc8e6263b428191228d9599ea9d Mon Sep 17 00:00:00 2001 From: "Leo (Sunpeng) Li" Date: Tue, 29 May 2018 09:51:51 -0400 Subject: [PATCH 048/285] drm/amd/display: Fix BUG_ON during CRTC atomic check update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For cases where the CRTC is inactive (DPMS off), where a modeset is not required, yet the CRTC is still in the atomic state, we should not attempt to update anything on it. Previously, we were relying on the modereset_required() helper to check the above condition. However, the function returns false immediately if a modeset is not required, ignoring the CRTC's enable/active state flags. The correct way to filter is by looking at these flags instead. Fixes: e277adc5a06c "drm/amd/display: Hookup color management functions" Bugzilla: https://bugs.freedesktop.org/106194 Signed-off-by: Leo (Sunpeng) Li Reviewed-by: Harry Wentland Tested-by: Michel Dänzer Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 1dd1142246c25f..ad1ad333012ab1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4695,15 +4695,16 @@ static int dm_update_crtcs_state(struct dc *dc, * We want to do dc stream updates that do not require a * full modeset below. */ - if (!enable || !aconnector || modereset_required(new_crtc_state)) + if (!(enable && aconnector && new_crtc_state->enable && + new_crtc_state->active)) continue; /* * Given above conditions, the dc state cannot be NULL because: - * 1. We're attempting to enable a CRTC. Which has a... - * 2. Valid connector attached, and - * 3. User does not want to reset it (disable or mark inactive, - * which can happen on a CRTC that's already disabled). - * => It currently exists. + * 1. We're in the process of enabling CRTCs (just been added + * to the dc context, or already is on the context) + * 2. Has a valid connector attached, and + * 3. Is currently active and enabled. + * => The dc stream state currently exists. */ BUG_ON(dm_new_crtc_state->stream == NULL); From 369b230806a9f3ff691466d54795e5e3ff3f8f5c Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Mon, 7 May 2018 14:13:43 -0700 Subject: [PATCH 049/285] perf parse-events: Handle uncore event aliases in small groups properly Perf stat doesn't count the uncore event aliases from the same uncore block in a group, for example: perf stat -e '{unc_m_cas_count.all,unc_m_clockticks}' -a -I 1000 # time counts unit events 1.000447342 unc_m_cas_count.all 1.000447342 unc_m_clockticks 2.000740654 unc_m_cas_count.all 2.000740654 unc_m_clockticks The output is very misleading. It gives a wrong impression that the uncore event doesn't work. An uncore block could be composed by several PMUs. An uncore event alias is a joint name which means the same event runs on all PMUs of a block. Perf doesn't support mixed events from different PMUs in the same group. It is wrong to put uncore event aliases in a big group. The right way is to split the big group into multiple small groups which only include the events from the same PMU. Only uncore event aliases from the same uncore block should be specially handled here. It doesn't make sense to mix the uncore events with other uncore events from different blocks or even core events in a group. With the patch: # time counts unit events 1.001557653 140,833 unc_m_cas_count.all 1.001557653 1,330,231,332 unc_m_clockticks 2.002709483 85,007 unc_m_cas_count.all 2.002709483 1,429,494,563 unc_m_clockticks Reported-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Jiri Olsa Cc: Agustin Vega-Frias Cc: Ganapatrao Kulkarni Cc: Jin Yao Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Shaokun Zhang Cc: Will Deacon Link: http://lkml.kernel.org/r/1525727623-19768-1-git-send-email-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.h | 1 + tools/perf/util/parse-events.c | 130 ++++++++++++++++++++++++++++++++- tools/perf/util/parse-events.h | 7 +- tools/perf/util/parse-events.y | 8 +- 4 files changed, 137 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 92ec009a292d3e..b13f5f234c8ffa 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -127,6 +127,7 @@ struct perf_evsel { bool precise_max; bool ignore_missing_thread; bool forced_leader; + bool use_uncore_alias; /* parse modifier helper */ int exclude_GH; int nr_members; diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b8b8a9558d325c..2fc4ee8b86c112 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1219,13 +1219,16 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, int parse_events_add_pmu(struct parse_events_state *parse_state, struct list_head *list, char *name, - struct list_head *head_config, bool auto_merge_stats) + struct list_head *head_config, + bool auto_merge_stats, + bool use_alias) { struct perf_event_attr attr; struct perf_pmu_info info; struct perf_pmu *pmu; struct perf_evsel *evsel; struct parse_events_error *err = parse_state->error; + bool use_uncore_alias; LIST_HEAD(config_terms); pmu = perf_pmu__find(name); @@ -1244,11 +1247,14 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, memset(&attr, 0, sizeof(attr)); } + use_uncore_alias = (pmu->is_uncore && use_alias); + if (!head_config) { attr.type = pmu->type; evsel = __add_event(list, &parse_state->idx, &attr, NULL, pmu, NULL, auto_merge_stats); if (evsel) { evsel->pmu_name = name; + evsel->use_uncore_alias = use_uncore_alias; return 0; } else { return -ENOMEM; @@ -1282,6 +1288,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, evsel->metric_expr = info.metric_expr; evsel->metric_name = info.metric_name; evsel->pmu_name = name; + evsel->use_uncore_alias = use_uncore_alias; } return evsel ? 0 : -ENOMEM; @@ -1317,7 +1324,8 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, list_add_tail(&term->list, head); if (!parse_events_add_pmu(parse_state, list, - pmu->name, head, true)) { + pmu->name, head, + true, true)) { pr_debug("%s -> %s/%s/\n", str, pmu->name, alias->str); ok++; @@ -1339,7 +1347,120 @@ int parse_events__modifier_group(struct list_head *list, return parse_events__modifier_event(list, event_mod, true); } -void parse_events__set_leader(char *name, struct list_head *list) +/* + * Check if the two uncore PMUs are from the same uncore block + * The format of the uncore PMU name is uncore_#blockname_#pmuidx + */ +static bool is_same_uncore_block(const char *pmu_name_a, const char *pmu_name_b) +{ + char *end_a, *end_b; + + end_a = strrchr(pmu_name_a, '_'); + end_b = strrchr(pmu_name_b, '_'); + + if (!end_a || !end_b) + return false; + + if ((end_a - pmu_name_a) != (end_b - pmu_name_b)) + return false; + + return (strncmp(pmu_name_a, pmu_name_b, end_a - pmu_name_a) == 0); +} + +static int +parse_events__set_leader_for_uncore_aliase(char *name, struct list_head *list, + struct parse_events_state *parse_state) +{ + struct perf_evsel *evsel, *leader; + uintptr_t *leaders; + bool is_leader = true; + int i, nr_pmu = 0, total_members, ret = 0; + + leader = list_first_entry(list, struct perf_evsel, node); + evsel = list_last_entry(list, struct perf_evsel, node); + total_members = evsel->idx - leader->idx + 1; + + leaders = calloc(total_members, sizeof(uintptr_t)); + if (WARN_ON(!leaders)) + return 0; + + /* + * Going through the whole group and doing sanity check. + * All members must use alias, and be from the same uncore block. + * Also, storing the leader events in an array. + */ + __evlist__for_each_entry(list, evsel) { + + /* Only split the uncore group which members use alias */ + if (!evsel->use_uncore_alias) + goto out; + + /* The events must be from the same uncore block */ + if (!is_same_uncore_block(leader->pmu_name, evsel->pmu_name)) + goto out; + + if (!is_leader) + continue; + /* + * If the event's PMU name starts to repeat, it must be a new + * event. That can be used to distinguish the leader from + * other members, even they have the same event name. + */ + if ((leader != evsel) && (leader->pmu_name == evsel->pmu_name)) { + is_leader = false; + continue; + } + /* The name is always alias name */ + WARN_ON(strcmp(leader->name, evsel->name)); + + /* Store the leader event for each PMU */ + leaders[nr_pmu++] = (uintptr_t) evsel; + } + + /* only one event alias */ + if (nr_pmu == total_members) { + parse_state->nr_groups--; + goto handled; + } + + /* + * An uncore event alias is a joint name which means the same event + * runs on all PMUs of a block. + * Perf doesn't support mixed events from different PMUs in the same + * group. The big group has to be split into multiple small groups + * which only include the events from the same PMU. + * + * Here the uncore event aliases must be from the same uncore block. + * The number of PMUs must be same for each alias. The number of new + * small groups equals to the number of PMUs. + * Setting the leader event for corresponding members in each group. + */ + i = 0; + __evlist__for_each_entry(list, evsel) { + if (i >= nr_pmu) + i = 0; + evsel->leader = (struct perf_evsel *) leaders[i++]; + } + + /* The number of members and group name are same for each group */ + for (i = 0; i < nr_pmu; i++) { + evsel = (struct perf_evsel *) leaders[i]; + evsel->nr_members = total_members / nr_pmu; + evsel->group_name = name ? strdup(name) : NULL; + } + + /* Take the new small groups into account */ + parse_state->nr_groups += nr_pmu - 1; + +handled: + ret = 1; +out: + free(leaders); + return ret; +} + +void parse_events__set_leader(char *name, struct list_head *list, + struct parse_events_state *parse_state) { struct perf_evsel *leader; @@ -1348,6 +1469,9 @@ void parse_events__set_leader(char *name, struct list_head *list) return; } + if (parse_events__set_leader_for_uncore_aliase(name, list, parse_state)) + return; + __perf_evlist__set_leader(list); leader = list_entry(list->next, struct perf_evsel, node); leader->group_name = name ? strdup(name) : NULL; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 5015cfd58277a6..4473dac27aee25 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -167,7 +167,9 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, void *ptr, char *type, u64 len); int parse_events_add_pmu(struct parse_events_state *parse_state, struct list_head *list, char *name, - struct list_head *head_config, bool auto_merge_stats); + struct list_head *head_config, + bool auto_merge_stats, + bool use_alias); int parse_events_multi_pmu_add(struct parse_events_state *parse_state, char *str, @@ -178,7 +180,8 @@ int parse_events_copy_term_list(struct list_head *old, enum perf_pmu_event_symbol_type perf_pmu__parse_check(const char *name); -void parse_events__set_leader(char *name, struct list_head *list); +void parse_events__set_leader(char *name, struct list_head *list, + struct parse_events_state *parse_state); void parse_events_update_lists(struct list_head *list_event, struct list_head *list_all); void parse_events_evlist_error(struct parse_events_state *parse_state, diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 7afeb80cc39eed..e37608a87dba5f 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -161,7 +161,7 @@ PE_NAME '{' events '}' struct list_head *list = $3; inc_group_count(list, _parse_state); - parse_events__set_leader($1, list); + parse_events__set_leader($1, list, _parse_state); $$ = list; } | @@ -170,7 +170,7 @@ PE_NAME '{' events '}' struct list_head *list = $2; inc_group_count(list, _parse_state); - parse_events__set_leader(NULL, list); + parse_events__set_leader(NULL, list, _parse_state); $$ = list; } @@ -232,7 +232,7 @@ PE_NAME opt_event_config YYABORT; ALLOC_LIST(list); - if (parse_events_add_pmu(_parse_state, list, $1, $2, false)) { + if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) { struct perf_pmu *pmu = NULL; int ok = 0; char *pattern; @@ -251,7 +251,7 @@ PE_NAME opt_event_config free(pattern); YYABORT; } - if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true)) + if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false)) ok++; parse_events_terms__delete(terms); } From d121109100bda84bbbb199dab97f9d56432ab235 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Mon, 28 May 2018 09:36:57 +0200 Subject: [PATCH 050/285] perf test: "Session topology" dumps core on s390 The "perf test Session topology" entry fails with core dump on s390. The root cause is a NULL pointer dereference in function check_cpu_topology() line 76 (or line 82 without -v). The session->header.env.cpu variable is NULL because on s390 function process_cpu_topology() returns with error: socket_id number is too big. You may need to upgrade the perf tool. and releases the env.cpu variable via zfree() and sets it to NULL. Here is the gdb output: (gdb) n 76 pr_debug("CPU %d, core %d, socket %d\n", i, (gdb) n Program received signal SIGSEGV, Segmentation fault. 0x00000000010f4d9e in check_cpu_topology (path=0x3ffffffd6c8 "/tmp/perf-test-J6CHMa", map=0x14a1740) at tests/topology.c:76 76 pr_debug("CPU %d, core %d, socket %d\n", i, (gdb) Make sure the env.cpu variable is not used when its NULL. Test for NULL pointer and return TEST_SKIP if so. Output before: [root@p23lp27 perf]# ./perf test -F 39 39: Session topology :Segmentation fault (core dumped) [root@p23lp27 perf]# Output after: [root@p23lp27 perf]# ./perf test -vF 39 39: Session topology : --- start --- templ file: /tmp/perf-test-Ajx59D socket_id number is too big.You may need to upgrade the perf tool. ---- end ---- Session topology: Skip [root@p23lp27 perf]# Signed-off-by: Thomas Richter Cc: Heiko Carstens Cc: Hendrik Brueckner Cc: Martin Schwidefsky Link: http://lkml.kernel.org/r/20180528073657.11743-1-tmricht@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/topology.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index 17cb1bb3448c84..40e30a26b23cc2 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -70,6 +70,27 @@ static int check_cpu_topology(char *path, struct cpu_map *map) session = perf_session__new(&data, false, NULL); TEST_ASSERT_VAL("can't get session", session); + /* On platforms with large numbers of CPUs process_cpu_topology() + * might issue an error while reading the perf.data file section + * HEADER_CPU_TOPOLOGY and the cpu_topology_map pointed to by member + * cpu is a NULL pointer. + * Example: On s390 + * CPU 0 is on core_id 0 and physical_package_id 6 + * CPU 1 is on core_id 1 and physical_package_id 3 + * + * Core_id and physical_package_id are platform and architecture + * dependend and might have higher numbers than the CPU id. + * This actually depends on the configuration. + * + * In this case process_cpu_topology() prints error message: + * "socket_id number is too big. You may need to upgrade the + * perf tool." + * + * This is the reason why this test might be skipped. + */ + if (!session->header.env.cpu) + return TEST_SKIP; + for (i = 0; i < session->header.env.nr_cpus_avail; i++) { if (!cpu_map__has(map, i)) continue; @@ -95,7 +116,7 @@ int test__session_topology(struct test *test __maybe_unused, int subtest __maybe { char path[PATH_MAX]; struct cpu_map *map; - int ret = -1; + int ret = TEST_FAIL; TEST_ASSERT_VAL("can't get templ file", !get_temp(path)); @@ -110,12 +131,9 @@ int test__session_topology(struct test *test __maybe_unused, int subtest __maybe goto free_path; } - if (check_cpu_topology(path, map)) - goto free_map; - ret = 0; - -free_map: + ret = check_cpu_topology(path, map); cpu_map__put(map); + free_path: unlink(path); return ret; From f5a4941aa6d190e676065e8f4ed35999f52a01c3 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 29 May 2018 14:18:19 +0800 Subject: [PATCH 051/285] vhost_net: flush batched heads before trying to busy polling After commit e2b3b35eb989 ("vhost_net: batch used ring update in rx"), we tend to batch updating used heads. But it doesn't flush batched heads before trying to do busy polling, this will cause vhost to wait for guest TX which waits for the used RX. Fixing by flush batched heads before busy loop. 1 byte TCP_RR performance recovers from 13107.83 to 50402.65. Fixes: e2b3b35eb989 ("vhost_net: batch used ring update in rx") Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/vhost/net.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 986058a57917f5..eeaf6739215f51 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -105,7 +105,9 @@ struct vhost_net_virtqueue { /* vhost zerocopy support fields below: */ /* last used idx for outstanding DMA zerocopy buffers */ int upend_idx; - /* first used idx for DMA done zerocopy buffers */ + /* For TX, first used idx for DMA done zerocopy buffers + * For RX, number of batched heads + */ int done_idx; /* an array of userspace buffers info */ struct ubuf_info *ubuf_info; @@ -626,6 +628,18 @@ static int sk_has_rx_data(struct sock *sk) return skb_queue_empty(&sk->sk_receive_queue); } +static void vhost_rx_signal_used(struct vhost_net_virtqueue *nvq) +{ + struct vhost_virtqueue *vq = &nvq->vq; + struct vhost_dev *dev = vq->dev; + + if (!nvq->done_idx) + return; + + vhost_add_used_and_signal_n(dev, vq, vq->heads, nvq->done_idx); + nvq->done_idx = 0; +} + static int vhost_net_rx_peek_head_len(struct vhost_net *net, struct sock *sk) { struct vhost_net_virtqueue *rvq = &net->vqs[VHOST_NET_VQ_RX]; @@ -635,6 +649,8 @@ static int vhost_net_rx_peek_head_len(struct vhost_net *net, struct sock *sk) int len = peek_head_len(rvq, sk); if (!len && vq->busyloop_timeout) { + /* Flush batched heads first */ + vhost_rx_signal_used(rvq); /* Both tx vq and rx socket were polled here */ mutex_lock_nested(&vq->mutex, 1); vhost_disable_notify(&net->dev, vq); @@ -762,7 +778,7 @@ static void handle_rx(struct vhost_net *net) }; size_t total_len = 0; int err, mergeable; - s16 headcount, nheads = 0; + s16 headcount; size_t vhost_hlen, sock_hlen; size_t vhost_len, sock_len; struct socket *sock; @@ -790,8 +806,8 @@ static void handle_rx(struct vhost_net *net) while ((sock_len = vhost_net_rx_peek_head_len(net, sock->sk))) { sock_len += sock_hlen; vhost_len = sock_len + vhost_hlen; - headcount = get_rx_bufs(vq, vq->heads + nheads, vhost_len, - &in, vq_log, &log, + headcount = get_rx_bufs(vq, vq->heads + nvq->done_idx, + vhost_len, &in, vq_log, &log, likely(mergeable) ? UIO_MAXIOV : 1); /* On error, stop handling until the next kick. */ if (unlikely(headcount < 0)) @@ -862,12 +878,9 @@ static void handle_rx(struct vhost_net *net) vhost_discard_vq_desc(vq, headcount); goto out; } - nheads += headcount; - if (nheads > VHOST_RX_BATCH) { - vhost_add_used_and_signal_n(&net->dev, vq, vq->heads, - nheads); - nheads = 0; - } + nvq->done_idx += headcount; + if (nvq->done_idx > VHOST_RX_BATCH) + vhost_rx_signal_used(nvq); if (unlikely(vq_log)) vhost_log_write(vq, vq_log, log, vhost_len); total_len += vhost_len; @@ -878,9 +891,7 @@ static void handle_rx(struct vhost_net *net) } vhost_net_enable_vq(net, vq); out: - if (nheads) - vhost_add_used_and_signal_n(&net->dev, vq, vq->heads, - nheads); + vhost_rx_signal_used(nvq); mutex_unlock(&vq->mutex); } From c32048d9e93a5ab925d745396c63e7b912147f0a Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 30 May 2018 11:43:58 +0200 Subject: [PATCH 052/285] drm/bridge/synopsys: dw-hdmi: fix dw_hdmi_setup_rx_sense The dw_hdmi_setup_rx_sense exported function should not use struct device to recover the dw-hdmi context using drvdata, but take struct dw_hdmi directly like other exported functions. This caused a regression using Meson DRM on S905X since v4.17-rc1 : Internal error: Oops: 96000007 [#1] PREEMPT SMP [...] CPU: 0 PID: 124 Comm: irq/32-dw_hdmi_ Not tainted 4.17.0-rc7 #2 Hardware name: Libre Technology CC (DT) [...] pc : osq_lock+0x54/0x188 lr : __mutex_lock.isra.0+0x74/0x530 [...] Process irq/32-dw_hdmi_ (pid: 124, stack limit = 0x00000000adf418cb) Call trace: osq_lock+0x54/0x188 __mutex_lock_slowpath+0x10/0x18 mutex_lock+0x30/0x38 __dw_hdmi_setup_rx_sense+0x28/0x98 dw_hdmi_setup_rx_sense+0x10/0x18 dw_hdmi_top_thread_irq+0x2c/0x50 irq_thread_fn+0x28/0x68 irq_thread+0x10c/0x1a0 kthread+0x128/0x130 ret_from_fork+0x10/0x18 Code: 34000964 d00050a2 51000484 9135c042 (f864d844) ---[ end trace 945641e1fbbc07da ]--- note: irq/32-dw_hdmi_[124] exited with preempt_count 1 genirq: exiting task "irq/32-dw_hdmi_" (124) is an active IRQ thread (irq 32) Fixes: eea034af90c6 ("drm/bridge/synopsys: dw-hdmi: don't clobber drvdata") Signed-off-by: Neil Armstrong Tested-by: Koen Kooi Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/1527673438-20643-1-git-send-email-narmstrong@baylibre.com --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 15 ++++----------- drivers/gpu/drm/meson/meson_dw_hdmi.c | 2 +- include/drm/bridge/dw_hdmi.h | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index ec8d0006ef7cc1..3c136f2b954fd1 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2077,7 +2077,7 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id) return ret; } -void __dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense) +void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense) { mutex_lock(&hdmi->mutex); @@ -2103,13 +2103,6 @@ void __dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense) } mutex_unlock(&hdmi->mutex); } - -void dw_hdmi_setup_rx_sense(struct device *dev, bool hpd, bool rx_sense) -{ - struct dw_hdmi *hdmi = dev_get_drvdata(dev); - - __dw_hdmi_setup_rx_sense(hdmi, hpd, rx_sense); -} EXPORT_SYMBOL_GPL(dw_hdmi_setup_rx_sense); static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) @@ -2145,9 +2138,9 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) */ if (intr_stat & (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) { - __dw_hdmi_setup_rx_sense(hdmi, - phy_stat & HDMI_PHY_HPD, - phy_stat & HDMI_PHY_RX_SENSE); + dw_hdmi_setup_rx_sense(hdmi, + phy_stat & HDMI_PHY_HPD, + phy_stat & HDMI_PHY_RX_SENSE); if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0) cec_notifier_set_phys_addr(hdmi->cec_notifier, diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c index a393095aac1a69..c9ad45686e7ae2 100644 --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -529,7 +529,7 @@ static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id) if (stat & HDMITX_TOP_INTR_HPD_RISE) hpd_connected = true; - dw_hdmi_setup_rx_sense(dw_hdmi->dev, hpd_connected, + dw_hdmi_setup_rx_sense(dw_hdmi->hdmi, hpd_connected, hpd_connected); drm_helper_hpd_irq_event(dw_hdmi->encoder.dev); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index dd2a8cf7d20b8d..ccb5aa8468e0bf 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -151,7 +151,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, const struct dw_hdmi_plat_data *plat_data); -void dw_hdmi_setup_rx_sense(struct device *dev, bool hpd, bool rx_sense); +void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); void dw_hdmi_audio_enable(struct dw_hdmi *hdmi); From ab4e32ff5aa797eaea551dbb67946e2fcb56cc7e Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 11 May 2018 19:21:42 +0800 Subject: [PATCH 053/285] perf bpf: Fix NULL return handling in bpf__prepare_load() bpf_object__open()/bpf_object__open_buffer can return error pointer or NULL, check the return values with IS_ERR_OR_NULL() in bpf__prepare_load and bpf__prepare_load_buffer Signed-off-by: YueHaibing Acked-by: Daniel Borkmann Cc: Alexander Shishkin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: netdev@vger.kernel.org Link: https://lkml.kernel.org/n/tip-psf4xwc09n62al2cb9s33v9h@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index af7ad814b2c3e0..cee658733e2c56 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -66,7 +66,7 @@ bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name) } obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, name); - if (IS_ERR(obj)) { + if (IS_ERR_OR_NULL(obj)) { pr_debug("bpf: failed to load buffer\n"); return ERR_PTR(-EINVAL); } @@ -102,14 +102,14 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source) pr_debug("bpf: successfull builtin compilation\n"); obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename); - if (!IS_ERR(obj) && llvm_param.dump_obj) + if (!IS_ERR_OR_NULL(obj) && llvm_param.dump_obj) llvm__dump_obj(filename, obj_buf, obj_buf_sz); free(obj_buf); } else obj = bpf_object__open(filename); - if (IS_ERR(obj)) { + if (IS_ERR_OR_NULL(obj)) { pr_debug("bpf: failed to load %s\n", filename); return obj; } From e2ab28521a588785c3e053098ffe607b5ff54634 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Fri, 25 May 2018 17:10:54 -0600 Subject: [PATCH 054/285] perf cs-etm: Fix indexing for decoder packet queue The tail of a queue is supposed to be pointing to the next available slot in a queue. In this implementation the tail is incremented before it is used and as such points to the last used element, something that has the immense advantage of centralizing tail management at a single location and eliminating a lot of redundant code. But this needs to be taken into consideration on the dequeueing side where the head also needs to be incremented before it is used, or the first available element of the queue will be skipped. Signed-off-by: Mathieu Poirier Tested-by: Leo Yan Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Robert Walker Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1527289854-10755-1-git-send-email-mathieu.poirier@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm-decoder/cs-etm-decoder.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index c8b98fa2299736..4d5fc374e7302e 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -96,11 +96,19 @@ int cs_etm_decoder__get_packet(struct cs_etm_decoder *decoder, /* Nothing to do, might as well just return */ if (decoder->packet_count == 0) return 0; + /* + * The queueing process in function cs_etm_decoder__buffer_packet() + * increments the tail *before* using it. This is somewhat counter + * intuitive but it has the advantage of centralizing tail management + * at a single location. Because of that we need to follow the same + * heuristic with the head, i.e we increment it before using its + * value. Otherwise the first element of the packet queue is not + * used. + */ + decoder->head = (decoder->head + 1) & (MAX_BUFFER - 1); *packet = decoder->packet_buffer[decoder->head]; - decoder->head = (decoder->head + 1) & (MAX_BUFFER - 1); - decoder->packet_count--; return 1; From 0c711138fa61188aa379210e9e08ac76838dea03 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Mon, 28 May 2018 09:44:33 +0200 Subject: [PATCH 055/285] perf data: Update documentation section on cpu topology Add an explanation of each cpu's core and socket identifier to the perf.data file format documentation. Signed-off-by: Thomas Richter Cc: Heiko Carstens Cc: Hendrik Brueckner Cc: Martin Schwidefsky Link: http://lkml.kernel.org/r/20180528074433.16652-1-tmricht@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf.data-file-format.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt index d00f0d51cab80e..c57904a526ced0 100644 --- a/tools/perf/Documentation/perf.data-file-format.txt +++ b/tools/perf/Documentation/perf.data-file-format.txt @@ -153,10 +153,18 @@ struct { HEADER_CPU_TOPOLOGY = 13, String lists defining the core and CPU threads topology. +The string lists are followed by a variable length array +which contains core_id and socket_id of each cpu. +The number of entries can be determined by the size of the +section minus the sizes of both string lists. struct { struct perf_header_string_list cores; /* Variable length */ struct perf_header_string_list threads; /* Variable length */ + struct { + uint32_t core_id; + uint32_t socket_id; + } cpus[nr]; /* Variable length records */ }; Example: From 943f32a0e8a4ea513dc68b00720a6c65842135e8 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 28 May 2018 16:45:01 +0800 Subject: [PATCH 056/285] perf script python: Add addr into perf sample dict ARM CoreSight auxtrace uses 'sample->addr' to record the target address for branch instructions, so the data of 'sample->addr' is required for tracing data analysis. This commit collects data of 'sample->addr' into perf sample dict, finally can be used for python script for parsing event. Signed-off-by: Leo Yan Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Jonathan Corbet Cc: Mathieu Poirier Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Robert Walker Cc: Tor Jeremiassen Cc: coresight@lists.linaro.org Cc: kim.phillips@arm.co Cc: linux-arm-kernel@lists.infradead.org Cc: linux-doc@vger.kernel.org Link: http://lkml.kernel.org/r/1527497103-3593-3-git-send-email-leo.yan@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/scripting-engines/trace-event-python.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 10dd5fce082b5e..7f8afacd08eeda 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -531,6 +531,8 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample, PyLong_FromUnsignedLongLong(sample->period)); pydict_set_item_string_decref(dict_sample, "phys_addr", PyLong_FromUnsignedLongLong(sample->phys_addr)); + pydict_set_item_string_decref(dict_sample, "addr", + PyLong_FromUnsignedLongLong(sample->addr)); set_sample_read_in_dict(dict_sample, sample, evsel); pydict_set_item_string_decref(dict, "sample", dict_sample); From 18a7057420f8b67f15d17087bf5c0863db752c8b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 25 May 2018 16:37:36 -0300 Subject: [PATCH 057/285] perf tools: Fix perf.data format description of NRCPUS header In the perf.data HEADER_CPUDESC feadure header we store first the number of available CPUs in the system, then the number of CPUs at the time of writing the header, not the other way around. Reported-by: Thomas-Mich Richter Acked-by: Andi Kleen Cc: Adrian Hunter Cc: David Ahern Cc: He Kuang Cc: Hendrik Brueckner Cc: Jin Yao Cc: Jiri Olsa Cc: Kim Phillips Cc: Lakshman Annadorai Cc: Namhyung Kim Cc: Simon Que Cc: Stephane Eranian Cc: Wang Nan Link: https://lkml.kernel.org/n/tip-j7o92acm2vnxjv70y4o3swoc@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf.data-file-format.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt index c57904a526ced0..dfb218feaad92d 100644 --- a/tools/perf/Documentation/perf.data-file-format.txt +++ b/tools/perf/Documentation/perf.data-file-format.txt @@ -111,8 +111,8 @@ A perf_header_string with the CPU architecture (uname -m) A structure defining the number of CPUs. struct nr_cpus { - uint32_t nr_cpus_online; uint32_t nr_cpus_available; /* CPUs not yet onlined */ + uint32_t nr_cpus_online; }; HEADER_CPUDESC = 8, From 829bc787c1a0403e4d886296dd4d90c5f9c1744a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 30 May 2018 19:43:53 -0700 Subject: [PATCH 058/285] fs: clear writeback errors in inode_init_always In inode_init_always(), we clear the inode mapping flags, which clears any retained error (AS_EIO, AS_ENOSPC) bits. Unfortunately, we do not also clear wb_err, which means that old mapping errors can leak through to new inodes. This is crucial for the XFS inode allocation path because we recycle old in-core inodes and we do not want error state from an old file to leak into the new file. This bug was discovered by running generic/036 and generic/047 in a loop and noticing that the EIOs generated by the collision of direct and buffered writes in generic/036 would survive the remount between 036 and 047, and get reported to the fsyncs (on different files!) in generic/047. Signed-off-by: Darrick J. Wong Reviewed-by: Jeff Layton Reviewed-by: Brian Foster --- fs/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/inode.c b/fs/inode.c index 13ceb98c3bd3b3..3b55391072f3d3 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -178,6 +178,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode) mapping->a_ops = &empty_aops; mapping->host = inode; mapping->flags = 0; + mapping->wb_err = 0; atomic_set(&mapping->i_mmap_writable, 0); mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE); mapping->private_data = NULL; From 38369f54d97dd7dc50c73a2797bfeb53c2e87d2d Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Thu, 31 May 2018 09:45:18 +0200 Subject: [PATCH 059/285] xfrm Fix potential error pointer dereference in xfrm_bundle_create. We may derference an invalid pointer in the error path of xfrm_bundle_create(). Fix this by returning this error pointer directly instead of assigning it to xdst0. Fixes: 45b018beddb6 ("ipsec: Create and use new helpers for dst child access.") Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 40b54cc64243b0..5f48251c1319aa 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1658,7 +1658,6 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, trailer_len -= xdst_prev->u.dst.xfrm->props.trailer_len; } -out: return &xdst0->u.dst; put_states: @@ -1667,8 +1666,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, free_dst: if (xdst0) dst_release_immediate(&xdst0->u.dst); - xdst0 = ERR_PTR(err); - goto out; + + return ERR_PTR(err); } static int xfrm_expand_policies(const struct flowi *fl, u16 family, From 175f0e25abeaa2218d431141ce19cf1de70fa82d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 25 Jul 2017 18:58:21 +0200 Subject: [PATCH 060/285] sched/core: Fix rules for running on online && !active CPUs As already enforced by the WARN() in __set_cpus_allowed_ptr(), the rules for running on an online && !active CPU are stricter than just being a kthread, you need to be a per-cpu kthread. If you're not strictly per-CPU, you have better CPUs to run on and don't need the partially booted one to get your work done. The exception is to allow smpboot threads to bootstrap the CPU itself and get kernel 'services' initialized before we allow userspace on it. Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Paul E. McKenney Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Tejun Heo Cc: Thomas Gleixner Fixes: 955dbdf4ce87 ("sched: Allow migrating kthreads into online but inactive CPUs") Link: http://lkml.kernel.org/r/20170725165821.cejhb7v2s3kecems@hirez.programming.kicks-ass.net Signed-off-by: Ingo Molnar --- kernel/sched/core.c | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 092f7c4de90366..1c58f54b911411 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -881,6 +881,33 @@ void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags) } #ifdef CONFIG_SMP + +static inline bool is_per_cpu_kthread(struct task_struct *p) +{ + if (!(p->flags & PF_KTHREAD)) + return false; + + if (p->nr_cpus_allowed != 1) + return false; + + return true; +} + +/* + * Per-CPU kthreads are allowed to run on !actie && online CPUs, see + * __set_cpus_allowed_ptr() and select_fallback_rq(). + */ +static inline bool is_cpu_allowed(struct task_struct *p, int cpu) +{ + if (!cpumask_test_cpu(cpu, &p->cpus_allowed)) + return false; + + if (is_per_cpu_kthread(p)) + return cpu_online(cpu); + + return cpu_active(cpu); +} + /* * This is how migration works: * @@ -938,16 +965,8 @@ struct migration_arg { static struct rq *__migrate_task(struct rq *rq, struct rq_flags *rf, struct task_struct *p, int dest_cpu) { - if (p->flags & PF_KTHREAD) { - if (unlikely(!cpu_online(dest_cpu))) - return rq; - } else { - if (unlikely(!cpu_active(dest_cpu))) - return rq; - } - /* Affinity changed (again). */ - if (!cpumask_test_cpu(dest_cpu, &p->cpus_allowed)) + if (!is_cpu_allowed(p, dest_cpu)) return rq; update_rq_clock(rq); @@ -1476,10 +1495,9 @@ static int select_fallback_rq(int cpu, struct task_struct *p) for (;;) { /* Any allowed, online CPU? */ for_each_cpu(dest_cpu, &p->cpus_allowed) { - if (!(p->flags & PF_KTHREAD) && !cpu_active(dest_cpu)) - continue; - if (!cpu_online(dest_cpu)) + if (!is_cpu_allowed(p, dest_cpu)) continue; + goto out; } From 7af443ee1697607541c6346c87385adab2214743 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Sat, 26 May 2018 08:46:47 -0700 Subject: [PATCH 061/285] sched/core: Require cpu_active() in select_task_rq(), for user tasks select_task_rq() is used in a few paths to select the CPU upon which a thread should be run - for example it is used by try_to_wake_up() & by fork or exec balancing. As-is it allows use of any online CPU that is present in the task's cpus_allowed mask. This presents a problem because there is a period whilst CPUs are brought online where a CPU is marked online, but is not yet fully initialized - ie. the period where CPUHP_AP_ONLINE_IDLE <= state < CPUHP_ONLINE. Usually we don't run any user tasks during this window, but there are corner cases where this can happen. An example observed is: - Some user task A, running on CPU X, forks to create task B. - sched_fork() calls __set_task_cpu() with cpu=X, setting task B's task_struct::cpu field to X. - CPU X is offlined. - Task A, currently somewhere between the __set_task_cpu() in copy_process() and the call to wake_up_new_task(), is migrated to CPU Y by migrate_tasks() when CPU X is offlined. - CPU X is onlined, but still in the CPUHP_AP_ONLINE_IDLE state. The scheduler is now active on CPU X, but there are no user tasks on the runqueue. - Task A runs on CPU Y & reaches wake_up_new_task(). This calls select_task_rq() with cpu=X, taken from task B's task_struct, and select_task_rq() allows CPU X to be returned. - Task A enqueues task B on CPU X's runqueue, via activate_task() & enqueue_task(). - CPU X now has a user task on its runqueue before it has reached the CPUHP_ONLINE state. In most cases, the user tasks that schedule on the newly onlined CPU have no idea that anything went wrong, but one case observed to be problematic is if the task goes on to invoke the sched_setaffinity syscall. The newly onlined CPU reaches the CPUHP_AP_ONLINE_IDLE state before the CPU that brought it online calls stop_machine_unpark(). This means that for a portion of the window of time between CPUHP_AP_ONLINE_IDLE & CPUHP_ONLINE the newly onlined CPU's struct cpu_stopper has its enabled field set to false. If a user thread is executed on the CPU during this window and it invokes sched_setaffinity with a CPU mask that does not include the CPU it's running on, then when __set_cpus_allowed_ptr() calls stop_one_cpu() intending to invoke migration_cpu_stop() and perform the actual migration away from the CPU it will simply return -ENOENT rather than calling migration_cpu_stop(). We then return from the sched_setaffinity syscall back to the user task that is now running on a CPU which it just asked not to run on, and which is not present in its cpus_allowed mask. This patch resolves the problem by having select_task_rq() enforce that user tasks run on CPUs that are active - the same requirement that select_fallback_rq() already enforces. This should ensure that newly onlined CPUs reach the CPUHP_AP_ACTIVE state before being able to schedule user tasks, and also implies that bringup_wait_for_ap() will have called stop_machine_unpark() which resolves the sched_setaffinity issue above. I haven't yet investigated them, but it may be of interest to review whether any of the actions performed by hotplug states between CPUHP_AP_ONLINE_IDLE & CPUHP_AP_ACTIVE could have similar unintended effects on user tasks that might schedule before they are reached, which might widen the scope of the problem from just affecting the behaviour of sched_setaffinity. Signed-off-by: Paul Burton Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20180526154648.11635-2-paul.burton@mips.com Signed-off-by: Ingo Molnar --- kernel/sched/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 1c58f54b911411..211890edf37e48 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1560,8 +1560,7 @@ int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags) * [ this allows ->select_task() to simply return task_cpu(p) and * not worry about this generic constraint ] */ - if (unlikely(!cpumask_test_cpu(cpu, &p->cpus_allowed) || - !cpu_online(cpu))) + if (unlikely(!is_cpu_allowed(p, cpu))) cpu = select_fallback_rq(task_cpu(p), p); return cpu; From ecda2b66e263dfd6c1d6113add19150f4e235bb3 Mon Sep 17 00:00:00 2001 From: Juri Lelli Date: Wed, 30 May 2018 18:08:09 +0200 Subject: [PATCH 062/285] sched/deadline: Fix missing clock update A missing clock update is causing the following warning: rq->clock_update_flags < RQCF_ACT_SKIP WARNING: CPU: 10 PID: 0 at kernel/sched/sched.h:963 inactive_task_timer+0x5d6/0x720 Call Trace: __hrtimer_run_queues+0x10f/0x530 hrtimer_interrupt+0xe5/0x240 smp_apic_timer_interrupt+0x79/0x2b0 apic_timer_interrupt+0xf/0x20 do_idle+0x203/0x280 cpu_startup_entry+0x6f/0x80 start_secondary+0x1b0/0x200 secondary_startup_64+0xa5/0xb0 hardirqs last enabled at (793919): [] cpuidle_enter_state+0x9e/0x360 hardirqs last disabled at (793920): [] interrupt_entry+0xce/0xe0 softirqs last enabled at (793922): [] irq_enter+0x68/0x70 softirqs last disabled at (793921): [] irq_enter+0x4d/0x70 This happens because inactive_task_timer() calls sub_running_bw() (if TASK_DEAD and non_contending) that might trigger a schedutil update, which might access the clock. Clock is however currently updated only later in inactive_task_timer() function. Fix the problem by updating the clock right after task_rq_lock(). Reported-by: kernel test robot Signed-off-by: Juri Lelli Signed-off-by: Peter Zijlstra (Intel) Cc: Claudio Scordino Cc: Linus Torvalds Cc: Luca Abeni Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20180530160809.9074-1-juri.lelli@redhat.com Signed-off-by: Ingo Molnar --- kernel/sched/deadline.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 1356afd1eeb6d6..fbfc3f1d368a08 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1259,6 +1259,9 @@ static enum hrtimer_restart inactive_task_timer(struct hrtimer *timer) rq = task_rq_lock(p, &rf); + sched_clock_tick(); + update_rq_clock(rq); + if (!dl_task(p) || p->state == TASK_DEAD) { struct dl_bw *dl_b = dl_bw_of(task_cpu(p)); @@ -1278,9 +1281,6 @@ static enum hrtimer_restart inactive_task_timer(struct hrtimer *timer) if (dl_se->dl_non_contending == 0) goto unlock; - sched_clock_tick(); - update_rq_clock(rq); - sub_running_bw(dl_se, &rq->dl); dl_se->dl_non_contending = 0; unlock: From 595058b6675e4d2a70dcd867c84d922975f9d22b Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 30 May 2018 15:49:40 -0700 Subject: [PATCH 063/285] sched/headers: Fix typo I cannot spell 'throttling'. Signed-off-by: Davidlohr Bueso Signed-off-by: Peter Zijlstra (Intel) Cc: Davidlohr Bueso Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20180530224940.17839-1-dave@stgolabs.net Signed-off-by: Ingo Molnar --- kernel/sched/sched.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 1f0a4bc6a39d20..cb467c221b15b9 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -983,7 +983,7 @@ static inline void rq_clock_skip_update(struct rq *rq) } /* - * See rt task throttoling, which is the only time a skip + * See rt task throttling, which is the only time a skip * request is cancelled. */ static inline void rq_clock_cancel_skipupdate(struct rq *rq) From a9e8d27574f26700575473011cb607d4abdbda5f Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 31 May 2018 13:48:31 -0400 Subject: [PATCH 064/285] drm/amd/display: Make atomic-check validate underscan changes When the underscan state was changed, atomic-check was triggering a validation but passing the old underscan values. This change adds a somewhat hacky check in dm_update_crtcs_state that will update the stream if old and newunderscan values are different. This was causing 4k on Fiji to allow underscan when it wasn't permitted. Signed-off-by: David Francis Reviewed-by: David Francis Acked-by: Harry Wentland Cc: stable@vger.kernel.org Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index ad1ad333012ab1..1a7e96ee6051b3 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4555,8 +4555,8 @@ static int dm_update_crtcs_state(struct dc *dc, for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { struct amdgpu_crtc *acrtc = NULL; struct amdgpu_dm_connector *aconnector = NULL; - struct drm_connector_state *new_con_state = NULL; - struct dm_connector_state *dm_conn_state = NULL; + struct drm_connector_state *drm_new_conn_state = NULL, *drm_old_conn_state = NULL; + struct dm_connector_state *dm_new_conn_state = NULL, *dm_old_conn_state = NULL; struct drm_plane_state *new_plane_state = NULL; new_stream = NULL; @@ -4577,19 +4577,23 @@ static int dm_update_crtcs_state(struct dc *dc, /* TODO This hack should go away */ if (aconnector && enable) { // Make sure fake sink is created in plug-in scenario - new_con_state = drm_atomic_get_connector_state(state, + drm_new_conn_state = drm_atomic_get_new_connector_state(state, &aconnector->base); + drm_old_conn_state = drm_atomic_get_old_connector_state(state, + &aconnector->base); - if (IS_ERR(new_con_state)) { - ret = PTR_ERR_OR_ZERO(new_con_state); + + if (IS_ERR(drm_new_conn_state)) { + ret = PTR_ERR_OR_ZERO(drm_new_conn_state); break; } - dm_conn_state = to_dm_connector_state(new_con_state); + dm_new_conn_state = to_dm_connector_state(drm_new_conn_state); + dm_old_conn_state = to_dm_connector_state(drm_old_conn_state); new_stream = create_stream_for_sink(aconnector, &new_crtc_state->mode, - dm_conn_state); + dm_new_conn_state); /* * we can have no stream on ACTION_SET if a display @@ -4708,6 +4712,11 @@ static int dm_update_crtcs_state(struct dc *dc, */ BUG_ON(dm_new_crtc_state->stream == NULL); + /* Scaling or underscan settings */ + if (is_scaling_state_different(dm_old_conn_state, dm_new_conn_state)) + update_stream_scaling_settings( + &new_crtc_state->mode, dm_new_conn_state, dm_new_crtc_state->stream); + /* Color managment settings */ if (dm_new_crtc_state->base.color_mgmt_changed) { ret = amdgpu_dm_set_regamma_lut(dm_new_crtc_state); From bc13f2f88eae63af943ab967cb14bb602f8f2eeb Mon Sep 17 00:00:00 2001 From: "Leo (Sunpeng) Li" Date: Thu, 31 May 2018 10:23:37 -0400 Subject: [PATCH 065/285] drm/amd/display: Update color props when modeset is required This fixes issues where color management properties don't persist over DPMS on/off, or when the CRTC is moved across connectors. Signed-off-by: Leo (Sunpeng) Li Reviewed-by: Harry Wentland Cc: stable@vger.kernel.org Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 1a7e96ee6051b3..27579443cdc5a7 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4717,8 +4717,12 @@ static int dm_update_crtcs_state(struct dc *dc, update_stream_scaling_settings( &new_crtc_state->mode, dm_new_conn_state, dm_new_crtc_state->stream); - /* Color managment settings */ - if (dm_new_crtc_state->base.color_mgmt_changed) { + /* + * Color management settings. We also update color properties + * when a modeset is needed, to ensure it gets reprogrammed. + */ + if (dm_new_crtc_state->base.color_mgmt_changed || + drm_atomic_crtc_needs_modeset(new_crtc_state)) { ret = amdgpu_dm_set_regamma_lut(dm_new_crtc_state); if (ret) goto fail; From 26de0b76d9ba3200f09c6cb9d9618bda338be5f7 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Wed, 30 May 2018 13:03:51 +1000 Subject: [PATCH 066/285] net/sonic: Use dma_mapping_error() With CONFIG_DMA_API_DEBUG=y, calling sonic_open() produces the message, "DMA-API: device driver failed to check map error". Add the missing dma_mapping_error() call. Cc: Thomas Bogendoerfer Signed-off-by: Finn Thain Acked-by: Thomas Bogendoerfer Signed-off-by: David S. Miller --- drivers/net/ethernet/natsemi/sonic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/natsemi/sonic.c b/drivers/net/ethernet/natsemi/sonic.c index 7ed08486ae23aa..c805dcbebd0278 100644 --- a/drivers/net/ethernet/natsemi/sonic.c +++ b/drivers/net/ethernet/natsemi/sonic.c @@ -84,7 +84,7 @@ static int sonic_open(struct net_device *dev) for (i = 0; i < SONIC_NUM_RRS; i++) { dma_addr_t laddr = dma_map_single(lp->device, skb_put(lp->rx_skb[i], SONIC_RBSIZE), SONIC_RBSIZE, DMA_FROM_DEVICE); - if (!laddr) { + if (dma_mapping_error(lp->device, laddr)) { while(i > 0) { /* free any that were mapped successfully */ i--; dma_unmap_single(lp->device, lp->rx_laddr[i], SONIC_RBSIZE, DMA_FROM_DEVICE); From 8258d2da9f9f521dce7019e018360c28d116354e Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Wed, 30 May 2018 11:29:15 +0300 Subject: [PATCH 067/285] cls_flower: Fix incorrect idr release when failing to modify rule When we fail to modify a rule, we incorrectly release the idr handle of the unmodified old rule. Fix that by checking if we need to release it. Fixes: fe2502e49b58 ("net_sched: remove cls_flower idr on failure") Reported-by: Vlad Buslov Reviewed-by: Roi Dayan Acked-by: Jiri Pirko Signed-off-by: Paul Blakey Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index d964e60c730eaf..c79f6e71512e5e 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -977,7 +977,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, return 0; errout_idr: - if (fnew->handle) + if (!fold) idr_remove(&head->handle_idr, fnew->handle); errout: tcf_exts_destroy(&fnew->exts); From 0f51f3582f22e543b78c4e113220ed1c35acbd97 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Thu, 31 May 2018 14:10:04 +1000 Subject: [PATCH 068/285] net/ncsi: Fix array size in dumpit handler With CONFIG_CC_STACKPROTECTOR enabled the kernel panics as below when parsing a NCSI_CMD_PKG_INFO command: [ 150.149711] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: 805cff08 [ 150.149711] [ 150.159919] CPU: 0 PID: 1301 Comm: ncsi-netlink Not tainted 4.13.16-468cbec6d2c91239332cb91b1f0a73aafcb6f0c6 #1 [ 150.170004] Hardware name: Generic DT based system [ 150.174852] [<80109930>] (unwind_backtrace) from [<80106bc4>] (show_stack+0x20/0x24) [ 150.182641] [<80106bc4>] (show_stack) from [<805d36e4>] (dump_stack+0x20/0x28) [ 150.189888] [<805d36e4>] (dump_stack) from [<801163ac>] (panic+0xdc/0x278) [ 150.196780] [<801163ac>] (panic) from [<801162cc>] (__stack_chk_fail+0x20/0x24) [ 150.204111] [<801162cc>] (__stack_chk_fail) from [<805cff08>] (ncsi_pkg_info_all_nl+0x244/0x258) [ 150.212912] [<805cff08>] (ncsi_pkg_info_all_nl) from [<804f939c>] (genl_lock_dumpit+0x3c/0x54) [ 150.221535] [<804f939c>] (genl_lock_dumpit) from [<804f873c>] (netlink_dump+0xf8/0x284) [ 150.229550] [<804f873c>] (netlink_dump) from [<804f8d44>] (__netlink_dump_start+0x124/0x17c) [ 150.237992] [<804f8d44>] (__netlink_dump_start) from [<804f9880>] (genl_rcv_msg+0x1c8/0x3d4) [ 150.246440] [<804f9880>] (genl_rcv_msg) from [<804f9174>] (netlink_rcv_skb+0xd8/0x134) [ 150.254361] [<804f9174>] (netlink_rcv_skb) from [<804f96a4>] (genl_rcv+0x30/0x44) [ 150.261850] [<804f96a4>] (genl_rcv) from [<804f7790>] (netlink_unicast+0x198/0x234) [ 150.269511] [<804f7790>] (netlink_unicast) from [<804f7ffc>] (netlink_sendmsg+0x368/0x3b0) [ 150.277783] [<804f7ffc>] (netlink_sendmsg) from [<804abea4>] (sock_sendmsg+0x24/0x34) [ 150.285625] [<804abea4>] (sock_sendmsg) from [<804ac1dc>] (___sys_sendmsg+0x244/0x260) [ 150.293556] [<804ac1dc>] (___sys_sendmsg) from [<804ad98c>] (__sys_sendmsg+0x5c/0x9c) [ 150.301400] [<804ad98c>] (__sys_sendmsg) from [<804ad9e4>] (SyS_sendmsg+0x18/0x1c) [ 150.308984] [<804ad9e4>] (SyS_sendmsg) from [<80102640>] (ret_fast_syscall+0x0/0x3c) [ 150.316743] ---[ end Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: 805cff08 This turns out to be because the attrs array in ncsi_pkg_info_all_nl() is initialised to a length of NCSI_ATTR_MAX which is the maximum attribute number, not the number of attributes. Fixes: 955dc68cb9b2 ("net/ncsi: Add generic netlink family") Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David S. Miller --- net/ncsi/ncsi-netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c index 8d7e849d482523..41cede4041d3cb 100644 --- a/net/ncsi/ncsi-netlink.c +++ b/net/ncsi/ncsi-netlink.c @@ -215,7 +215,7 @@ static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info) static int ncsi_pkg_info_all_nl(struct sk_buff *skb, struct netlink_callback *cb) { - struct nlattr *attrs[NCSI_ATTR_MAX]; + struct nlattr *attrs[NCSI_ATTR_MAX + 1]; struct ncsi_package *np, *package; struct ncsi_dev_priv *ndp; unsigned int package_id; From 8005b09d99fac78e6f5fb9da30b5ae94840af03b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 31 May 2018 09:44:49 +0300 Subject: [PATCH 069/285] net: ethernet: davinci_emac: fix error handling in probe() The current error handling code has an issue where it does: if (priv->txchan) cpdma_chan_destroy(priv->txchan); The problem is that ->txchan is either valid or an error pointer (which would lead to an Oops). I've changed it to use multiple error labels so that the test can be removed. Also there were some missing calls to netif_napi_del(). Fixes: 3ef0fdb2342c ("net: davinci_emac: switch to new cpdma layer") Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_emac.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index abceea802ea1b0..38828ab77eb9cb 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1873,7 +1873,7 @@ static int davinci_emac_probe(struct platform_device *pdev) if (IS_ERR(priv->txchan)) { dev_err(&pdev->dev, "error initializing tx dma channel\n"); rc = PTR_ERR(priv->txchan); - goto no_cpdma_chan; + goto err_free_dma; } priv->rxchan = cpdma_chan_create(priv->dma, EMAC_DEF_RX_CH, @@ -1881,14 +1881,14 @@ static int davinci_emac_probe(struct platform_device *pdev) if (IS_ERR(priv->rxchan)) { dev_err(&pdev->dev, "error initializing rx dma channel\n"); rc = PTR_ERR(priv->rxchan); - goto no_cpdma_chan; + goto err_free_txchan; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(&pdev->dev, "error getting irq res\n"); rc = -ENOENT; - goto no_cpdma_chan; + goto err_free_rxchan; } ndev->irq = res->start; @@ -1914,7 +1914,7 @@ static int davinci_emac_probe(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); dev_err(&pdev->dev, "%s: failed to get_sync(%d)\n", __func__, rc); - goto no_cpdma_chan; + goto err_napi_del; } /* register the network device */ @@ -1924,7 +1924,7 @@ static int davinci_emac_probe(struct platform_device *pdev) dev_err(&pdev->dev, "error in register_netdev\n"); rc = -ENODEV; pm_runtime_put(&pdev->dev); - goto no_cpdma_chan; + goto err_napi_del; } @@ -1937,11 +1937,13 @@ static int davinci_emac_probe(struct platform_device *pdev) return 0; -no_cpdma_chan: - if (priv->txchan) - cpdma_chan_destroy(priv->txchan); - if (priv->rxchan) - cpdma_chan_destroy(priv->rxchan); +err_napi_del: + netif_napi_del(&priv->napi); +err_free_rxchan: + cpdma_chan_destroy(priv->rxchan); +err_free_txchan: + cpdma_chan_destroy(priv->txchan); +err_free_dma: cpdma_ctlr_destroy(priv->dma); no_pdata: if (of_phy_is_fixed_link(np)) From 16e6653c8259ca5383126e1c6bcf7fe062af87fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hlavat=C3=BD?= Date: Thu, 31 May 2018 23:21:04 +0200 Subject: [PATCH 070/285] ixgbe: fix parsing of TC actions for HW offload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous code was optimistic, accepting the offload of whole action chain when there was a single known action (drop/redirect). This results in offloading a rule which should not be offloaded, because its behavior cannot be reproduced in the hardware. For example: $ tc filter add dev eno1 parent ffff: protocol ip \ u32 ht 800: order 1 match tcp src 42 FFFF \ action mirred egress mirror dev enp1s16 pipe \ drop The controller is unable to mirror the packet to a VF, but still offloads the rule by dropping the packet. Change the approach of the function to a pessimistic one, rejecting the chain when an unknown action is found. This is better suited for future extensions. Note that both recognized actions always return TC_ACT_SHOT, therefore it is safe to ignore actions behind them. Signed-off-by: OndÅ™ej Hlavatý Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index afadba99f7b828..2ecd55856c502c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9054,7 +9054,6 @@ static int parse_tc_actions(struct ixgbe_adapter *adapter, { const struct tc_action *a; LIST_HEAD(actions); - int err; if (!tcf_exts_has_actions(exts)) return -EINVAL; @@ -9075,11 +9074,11 @@ static int parse_tc_actions(struct ixgbe_adapter *adapter, if (!dev) return -EINVAL; - err = handle_redirect_action(adapter, dev->ifindex, queue, - action); - if (err == 0) - return err; + return handle_redirect_action(adapter, dev->ifindex, + queue, action); } + + return -EINVAL; } return -EINVAL; From 664088f8d68178809b848ca450f2797efb34e8e7 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 31 May 2018 15:59:46 -0400 Subject: [PATCH 071/285] net-sysfs: Fix memory leak in XPS configuration This patch reorders the error cases in showing the XPS configuration so that we hold off on memory allocation until after we have verified that we can support XPS on a given ring. Fixes: 184c449f91fe ("net: Add support for XPS with QoS via traffic classes") Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index c476f07941323f..bb7e80f4ced374 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1214,9 +1214,6 @@ static ssize_t xps_cpus_show(struct netdev_queue *queue, cpumask_var_t mask; unsigned long index; - if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) - return -ENOMEM; - index = get_netdev_queue_index(queue); if (dev->num_tc) { @@ -1226,6 +1223,9 @@ static ssize_t xps_cpus_show(struct netdev_queue *queue, return -EINVAL; } + if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; + rcu_read_lock(); dev_maps = rcu_dereference(dev->xps_maps); if (dev_maps) { From eb7f54b90bd8f469834c5e86dcf72ebf9a629811 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 1 Jun 2018 14:30:38 +0300 Subject: [PATCH 072/285] kcm: Fix use-after-free caused by clonned sockets (resend for properly queueing in patchwork) kcm_clone() creates kernel socket, which does not take net counter. Thus, the net may die before the socket is completely destructed, i.e. kcm_exit_net() is executed before kcm_done(). Reported-by: syzbot+5f1a04e374a635efc426@syzkaller.appspotmail.com Signed-off-by: Kirill Tkhai Signed-off-by: David S. Miller --- net/kcm/kcmsock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index dc76bc34682901..d3601d421571b9 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -1671,7 +1671,7 @@ static struct file *kcm_clone(struct socket *osock) __module_get(newsock->ops->owner); newsk = sk_alloc(sock_net(osock->sk), PF_KCM, GFP_KERNEL, - &kcm_proto, true); + &kcm_proto, false); if (!newsk) { sock_release(newsock); return ERR_PTR(-ENOMEM); From a95691bc54af1ac4b12c354f91e9cabf1cb068df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damien=20Th=C3=A9bault?= Date: Thu, 31 May 2018 07:04:01 +0000 Subject: [PATCH 073/285] net: dsa: b53: Add BCM5389 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for the BCM5389 switch connected through MDIO. Signed-off-by: Damien Thébault Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/dsa/b53.txt | 1 + drivers/net/dsa/b53/b53_common.c | 13 +++++++++++++ drivers/net/dsa/b53/b53_mdio.c | 5 ++++- drivers/net/dsa/b53/b53_priv.h | 1 + 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/dsa/b53.txt b/Documentation/devicetree/bindings/net/dsa/b53.txt index 8acf51a4dfa8a5..47a6a7fe0b8643 100644 --- a/Documentation/devicetree/bindings/net/dsa/b53.txt +++ b/Documentation/devicetree/bindings/net/dsa/b53.txt @@ -10,6 +10,7 @@ Required properties: "brcm,bcm53128" "brcm,bcm5365" "brcm,bcm5395" + "brcm,bcm5389" "brcm,bcm5397" "brcm,bcm5398" diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 78616787f2a396..3da5fca77cbdd7 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1711,6 +1711,18 @@ static const struct b53_chip_data b53_switch_chips[] = { .cpu_port = B53_CPU_PORT_25, .duplex_reg = B53_DUPLEX_STAT_FE, }, + { + .chip_id = BCM5389_DEVICE_ID, + .dev_name = "BCM5389", + .vlans = 4096, + .enabled_ports = 0x1f, + .arl_entries = 4, + .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, + }, { .chip_id = BCM5395_DEVICE_ID, .dev_name = "BCM5395", @@ -2034,6 +2046,7 @@ int b53_switch_detect(struct b53_device *dev) else dev->chip_id = BCM5365_DEVICE_ID; break; + case BCM5389_DEVICE_ID: case BCM5395_DEVICE_ID: case BCM5397_DEVICE_ID: case BCM5398_DEVICE_ID: diff --git a/drivers/net/dsa/b53/b53_mdio.c b/drivers/net/dsa/b53/b53_mdio.c index fa7556f5d4fb1b..a533a90e39048d 100644 --- a/drivers/net/dsa/b53/b53_mdio.c +++ b/drivers/net/dsa/b53/b53_mdio.c @@ -285,6 +285,7 @@ static const struct b53_io_ops b53_mdio_ops = { #define B53_BRCM_OUI_1 0x0143bc00 #define B53_BRCM_OUI_2 0x03625c00 #define B53_BRCM_OUI_3 0x00406000 +#define B53_BRCM_OUI_4 0x01410c00 static int b53_mdio_probe(struct mdio_device *mdiodev) { @@ -311,7 +312,8 @@ static int b53_mdio_probe(struct mdio_device *mdiodev) */ if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 && (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 && - (phy_id & 0xfffffc00) != B53_BRCM_OUI_3) { + (phy_id & 0xfffffc00) != B53_BRCM_OUI_3 && + (phy_id & 0xfffffc00) != B53_BRCM_OUI_4) { dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id); return -ENODEV; } @@ -360,6 +362,7 @@ static const struct of_device_id b53_of_match[] = { { .compatible = "brcm,bcm53125" }, { .compatible = "brcm,bcm53128" }, { .compatible = "brcm,bcm5365" }, + { .compatible = "brcm,bcm5389" }, { .compatible = "brcm,bcm5395" }, { .compatible = "brcm,bcm5397" }, { .compatible = "brcm,bcm5398" }, diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 1187ebd79287be..3b57f47d0e79aa 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -48,6 +48,7 @@ struct b53_io_ops { enum { BCM5325_DEVICE_ID = 0x25, BCM5365_DEVICE_ID = 0x65, + BCM5389_DEVICE_ID = 0x89, BCM5395_DEVICE_ID = 0x95, BCM5397_DEVICE_ID = 0x97, BCM5398_DEVICE_ID = 0x98, From 82612de1c98e610d194e34178bde3cca7dedce41 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 31 May 2018 10:59:32 +0200 Subject: [PATCH 074/285] ip_tunnel: restore binding to ifaces with a large mtu After commit f6cc9c054e77, the following conf is broken (note that the default loopback mtu is 65536, ie IP_MAX_MTU + 1): $ ip tunnel add gre1 mode gre local 10.125.0.1 remote 10.125.0.2 dev lo add tunnel "gre0" failed: Invalid argument $ ip l a type dummy $ ip l s dummy1 up $ ip l s dummy1 mtu 65535 $ ip tunnel add gre1 mode gre local 10.125.0.1 remote 10.125.0.2 dev dummy1 add tunnel "gre0" failed: Invalid argument dev_set_mtu() doesn't allow to set a mtu which is too large. First, let's cap the mtu returned by ip_tunnel_bind_dev(). Second, remove the magic value 0xFFF8 and use IP_MAX_MTU instead. 0xFFF8 seems to be there for ages, I don't know why this value was used. With a recent kernel, it's also possible to set a mtu > IP_MAX_MTU: $ ip l s dummy1 mtu 66000 After that patch, it's also possible to bind an ip tunnel on that kind of interface. CC: Petr Machata CC: Ido Schimmel Link: https://git.kernel.org/pub/scm/linux/kernel/git/davem/netdev-vger-cvs.git/commit/?id=e5afd356a411a Fixes: f6cc9c054e77 ("ip_tunnel: Emit events for post-register MTU changes") Signed-off-by: Nicolas Dichtel Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- net/ipv4/ip_tunnel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 6b0e362cc99b5d..38d906baf1df83 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -328,7 +328,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev) if (tdev) { hlen = tdev->hard_header_len + tdev->needed_headroom; - mtu = tdev->mtu; + mtu = min(tdev->mtu, IP_MAX_MTU); } dev->needed_headroom = t_hlen + hlen; @@ -362,7 +362,7 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net, nt = netdev_priv(dev); t_hlen = nt->hlen + sizeof(struct iphdr); dev->min_mtu = ETH_MIN_MTU; - dev->max_mtu = 0xFFF8 - dev->hard_header_len - t_hlen; + dev->max_mtu = IP_MAX_MTU - dev->hard_header_len - t_hlen; ip_tunnel_add(itn, nt); return nt; @@ -930,7 +930,7 @@ int __ip_tunnel_change_mtu(struct net_device *dev, int new_mtu, bool strict) { struct ip_tunnel *tunnel = netdev_priv(dev); int t_hlen = tunnel->hlen + sizeof(struct iphdr); - int max_mtu = 0xFFF8 - dev->hard_header_len - t_hlen; + int max_mtu = IP_MAX_MTU - dev->hard_header_len - t_hlen; if (new_mtu < ETH_MIN_MTU) return -EINVAL; @@ -1107,7 +1107,7 @@ int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], mtu = ip_tunnel_bind_dev(dev); if (tb[IFLA_MTU]) { - unsigned int max = 0xfff8 - dev->hard_header_len - nt->hlen; + unsigned int max = IP_MAX_MTU - dev->hard_header_len - nt->hlen; mtu = clamp(dev->mtu, (unsigned int)ETH_MIN_MTU, (unsigned int)(max - sizeof(struct iphdr))); From f7ff1fde9441b4fcc8ffb6e66e6e5a00d008937e Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 31 May 2018 10:59:33 +0200 Subject: [PATCH 075/285] ip6_tunnel: remove magic mtu value 0xFFF8 I don't know where this value comes from (probably a copy and paste and paste and paste ...). Let's use standard values which are a bit greater. Link: https://git.kernel.org/pub/scm/linux/kernel/git/davem/netdev-vger-cvs.git/commit/?id=e5afd356a411a Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- net/ipv6/ip6_tunnel.c | 11 ++++++++--- net/ipv6/sit.c | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index da66aaac51cecb..00e138a44cbba2 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1692,8 +1692,13 @@ int ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) if (new_mtu < ETH_MIN_MTU) return -EINVAL; } - if (new_mtu > 0xFFF8 - dev->hard_header_len) - return -EINVAL; + if (tnl->parms.proto == IPPROTO_IPV6 || tnl->parms.proto == 0) { + if (new_mtu > IP6_MAX_MTU - dev->hard_header_len) + return -EINVAL; + } else { + if (new_mtu > IP_MAX_MTU - dev->hard_header_len) + return -EINVAL; + } dev->mtu = new_mtu; return 0; } @@ -1841,7 +1846,7 @@ ip6_tnl_dev_init_gen(struct net_device *dev) if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) dev->mtu -= 8; dev->min_mtu = ETH_MIN_MTU; - dev->max_mtu = 0xFFF8 - dev->hard_header_len; + dev->max_mtu = IP6_MAX_MTU - dev->hard_header_len; return 0; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 2afce37a71776f..e9400ffa7875c7 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1371,7 +1371,7 @@ static void ipip6_tunnel_setup(struct net_device *dev) dev->hard_header_len = LL_MAX_HEADER + t_hlen; dev->mtu = ETH_DATA_LEN - t_hlen; dev->min_mtu = IPV6_MIN_MTU; - dev->max_mtu = 0xFFF8 - t_hlen; + dev->max_mtu = IP6_MAX_MTU - t_hlen; dev->flags = IFF_NOARP; netif_keep_dst(dev); dev->addr_len = 4; @@ -1583,7 +1583,8 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev, if (tb[IFLA_MTU]) { u32 mtu = nla_get_u32(tb[IFLA_MTU]); - if (mtu >= IPV6_MIN_MTU && mtu <= 0xFFF8 - dev->hard_header_len) + if (mtu >= IPV6_MIN_MTU && + mtu <= IP6_MAX_MTU - dev->hard_header_len) dev->mtu = mtu; } From 9f7c728332e8966084242fcd951aa46583bc308c Mon Sep 17 00:00:00 2001 From: Daniele Palmas Date: Thu, 31 May 2018 11:18:29 +0200 Subject: [PATCH 076/285] net: usb: cdc_mbim: add flag FLAG_SEND_ZLP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testing Telit LM940 with ICMP packets > 14552 bytes revealed that the modem needs FLAG_SEND_ZLP to properly work, otherwise the cdc mbim data interface won't be anymore responsive. Signed-off-by: Daniele Palmas Acked-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_mbim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 7220cd62071726..0362acd5cdcaaf 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -609,7 +609,7 @@ static const struct driver_info cdc_mbim_info_ndp_to_end = { */ static const struct driver_info cdc_mbim_info_avoid_altsetting_toggle = { .description = "CDC MBIM", - .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, + .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN | FLAG_SEND_ZLP, .bind = cdc_mbim_bind, .unbind = cdc_mbim_unbind, .manage_power = cdc_mbim_manage_power, From 36f9814a494a874d5a0f44843544b4b2539022db Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 2 Jun 2018 05:21:59 +0200 Subject: [PATCH 077/285] bpf: fix uapi hole for 32 bit compat applications In 64 bit, we have a 4 byte hole between ifindex and netns_dev in the case of struct bpf_map_info but also struct bpf_prog_info. In net-next commit b85fab0e67b ("bpf: Add gpl_compatible flag to struct bpf_prog_info") added a bitfield into it to expose some flags related to programs. Thus, add an unnamed __u32 bitfield for both so that alignment keeps the same in both 32 and 64 bit cases, and can be naturally extended from there as in b85fab0e67b. Before: # file test.o test.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped # pahole test.o struct bpf_map_info { __u32 type; /* 0 4 */ __u32 id; /* 4 4 */ __u32 key_size; /* 8 4 */ __u32 value_size; /* 12 4 */ __u32 max_entries; /* 16 4 */ __u32 map_flags; /* 20 4 */ char name[16]; /* 24 16 */ __u32 ifindex; /* 40 4 */ __u64 netns_dev; /* 44 8 */ __u64 netns_ino; /* 52 8 */ /* size: 64, cachelines: 1, members: 10 */ /* padding: 4 */ }; After (same as on 64 bit): # file test.o test.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped # pahole test.o struct bpf_map_info { __u32 type; /* 0 4 */ __u32 id; /* 4 4 */ __u32 key_size; /* 8 4 */ __u32 value_size; /* 12 4 */ __u32 max_entries; /* 16 4 */ __u32 map_flags; /* 20 4 */ char name[16]; /* 24 16 */ __u32 ifindex; /* 40 4 */ /* XXX 4 bytes hole, try to pack */ __u64 netns_dev; /* 48 8 */ __u64 netns_ino; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ /* size: 64, cachelines: 1, members: 10 */ /* sum members: 60, holes: 1, sum holes: 4 */ }; Reported-by: Dmitry V. Levin Reported-by: Eugene Syromiatnikov Fixes: 52775b33bb507 ("bpf: offload: report device information about offloaded maps") Fixes: 675fc275a3a2d ("bpf: offload: report device information for offloaded programs") Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: Alexei Starovoitov --- include/uapi/linux/bpf.h | 2 ++ tools/include/uapi/linux/bpf.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index c5ec89732a8d92..8c317737ba3f01 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -1017,6 +1017,7 @@ struct bpf_prog_info { __aligned_u64 map_ids; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; + __u32 :32; __u64 netns_dev; __u64 netns_ino; } __attribute__((aligned(8))); @@ -1030,6 +1031,7 @@ struct bpf_map_info { __u32 map_flags; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; + __u32 :32; __u64 netns_dev; __u64 netns_ino; } __attribute__((aligned(8))); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index c5ec89732a8d92..8c317737ba3f01 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1017,6 +1017,7 @@ struct bpf_prog_info { __aligned_u64 map_ids; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; + __u32 :32; __u64 netns_dev; __u64 netns_ino; } __attribute__((aligned(8))); @@ -1030,6 +1031,7 @@ struct bpf_map_info { __u32 map_flags; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; + __u32 :32; __u64 netns_dev; __u64 netns_ino; } __attribute__((aligned(8))); From 89c29def6b0101fff66a3d74d0178b844f88d732 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Sat, 2 Jun 2018 08:41:44 -0600 Subject: [PATCH 078/285] Revert "vfio/type1: Improve memory pinning process for raw PFN mapping" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bisection by Amadeusz SÅ‚awiÅ„ski implicates this commit leading to bad page state issues after VM shutdown, likely due to unbalanced page references. The original commit was intended only as a performance improvement, therefore revert for offline rework. Link: https://lkml.org/lkml/2018/6/2/97 Fixes: 356e88ebe447 ("vfio/type1: Improve memory pinning process for raw PFN mapping") Cc: Jason Cai (Xiang Feng) Reported-by: Amadeusz SÅ‚awiÅ„ski Signed-off-by: Alex Williamson --- drivers/vfio/vfio_iommu_type1.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 5c212bf29640d8..3c082451ab1a00 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -404,6 +404,7 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, { unsigned long pfn = 0; long ret, pinned = 0, lock_acct = 0; + bool rsvd; dma_addr_t iova = vaddr - dma->vaddr + dma->iova; /* This code path is only user initiated */ @@ -414,23 +415,14 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, if (ret) return ret; - if (is_invalid_reserved_pfn(*pfn_base)) { - struct vm_area_struct *vma; - - down_read(¤t->mm->mmap_sem); - vma = find_vma_intersection(current->mm, vaddr, vaddr + 1); - pinned = min_t(long, npage, vma_pages(vma)); - up_read(¤t->mm->mmap_sem); - return pinned; - } - pinned++; + rsvd = is_invalid_reserved_pfn(*pfn_base); /* * Reserved pages aren't counted against the user, externally pinned * pages are already counted against the user. */ - if (!vfio_find_vpfn(dma, iova)) { + if (!rsvd && !vfio_find_vpfn(dma, iova)) { if (!lock_cap && current->mm->locked_vm + 1 > limit) { put_pfn(*pfn_base, dma->prot); pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__, @@ -450,12 +442,13 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, if (ret) break; - if (pfn != *pfn_base + pinned) { + if (pfn != *pfn_base + pinned || + rsvd != is_invalid_reserved_pfn(pfn)) { put_pfn(pfn, dma->prot); break; } - if (!vfio_find_vpfn(dma, iova)) { + if (!rsvd && !vfio_find_vpfn(dma, iova)) { if (!lock_cap && current->mm->locked_vm + lock_acct + 1 > limit) { put_pfn(pfn, dma->prot); @@ -473,8 +466,10 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, unpin_out: if (ret) { - for (pfn = *pfn_base ; pinned ; pfn++, pinned--) - put_pfn(pfn, dma->prot); + if (!rsvd) { + for (pfn = *pfn_base ; pinned ; pfn++, pinned--) + put_pfn(pfn, dma->prot); + } return ret; } From 2d077d4b59924acd1f5180c6fb73b57f4771fde6 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Fri, 1 Jun 2018 16:50:45 -0700 Subject: [PATCH 079/285] mm/huge_memory.c: __split_huge_page() use atomic ClearPageDirty() Swapping load on huge=always tmpfs (with khugepaged tuned up to be very eager, but I'm not sure that is relevant) soon hung uninterruptibly, waiting for page lock in shmem_getpage_gfp()'s find_lock_entry(), most often when "cp -a" was trying to write to a smallish file. Debug showed that the page in question was not locked, and page->mapping NULL by now, but page->index consistent with having been in a huge page before. Reproduced in minutes on a 4.15 kernel, even with 4.17's 605ca5ede764 ("mm/huge_memory.c: reorder operations in __split_huge_page_tail()") added in; but took hours to reproduce on a 4.17 kernel (no idea why). The culprit proved to be the __ClearPageDirty() on tails beyond i_size in __split_huge_page(): the non-atomic __bitoperation may have been safe when 4.8's baa355fd3314 ("thp: file pages support for split_huge_page()") introduced it, but liable to erase PageWaiters after 4.10's 62906027091f ("mm: add PageWaiters indicating tasks are waiting for a page bit"). Link: http://lkml.kernel.org/r/alpine.LSU.2.11.1805291841070.3197@eggly.anvils Fixes: 62906027091f ("mm: add PageWaiters indicating tasks are waiting for a page bit") Signed-off-by: Hugh Dickins Acked-by: Kirill A. Shutemov Cc: Konstantin Khlebnikov Cc: Nicholas Piggin Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/huge_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index a3a1815f8e1181..b9f3dbd885bda5 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2431,7 +2431,7 @@ static void __split_huge_page(struct page *page, struct list_head *list, __split_huge_page_tail(head, i, lruvec, list); /* Some pages can be beyond i_size: drop them from page cache */ if (head[i].index >= end) { - __ClearPageDirty(head + i); + ClearPageDirty(head + i); __delete_from_page_cache(head + i, NULL); if (IS_ENABLED(CONFIG_SHMEM) && PageSwapBacked(head)) shmem_uncharge(head->mapping->host, 1); From 145e1a71e090575c74969e3daa8136d1e5b99fc8 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Fri, 1 Jun 2018 16:50:50 -0700 Subject: [PATCH 080/285] mm: fix the NULL mapping case in __isolate_lru_page() George Boole would have noticed a slight error in 4.16 commit 69d763fc6d3a ("mm: pin address_space before dereferencing it while isolating an LRU page"). Fix it, to match both the comment above it, and the original behaviour. Although anonymous pages are not marked PageDirty at first, we have an old habit of calling SetPageDirty when a page is removed from swap cache: so there's a category of ex-swap pages that are easily migratable, but were inadvertently excluded from compaction's async migration in 4.16. Link: http://lkml.kernel.org/r/alpine.LSU.2.11.1805302014001.12558@eggly.anvils Fixes: 69d763fc6d3a ("mm: pin address_space before dereferencing it while isolating an LRU page") Signed-off-by: Hugh Dickins Acked-by: Minchan Kim Acked-by: Mel Gorman Reported-by: Ivan Kalvachev Cc: "Huang, Ying" Cc: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 9b697323a88c98..9270a4370d542b 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1418,7 +1418,7 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode) return ret; mapping = page_mapping(page); - migrate_dirty = mapping && mapping->a_ops->migratepage; + migrate_dirty = !mapping || mapping->a_ops->migratepage; unlock_page(page); if (!migrate_dirty) return ret; From af04fadcaa932d2d804699409d9d96dd5d85ce7f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Jun 2018 01:31:02 -0400 Subject: [PATCH 081/285] Revert "fs: fold open_check_o_direct into do_dentry_open" This reverts commit cab64df194667dc5d9d786f0a895f647f5501c0d. Having vfs_open() in some cases drop the reference to struct file combined with error = vfs_open(path, f, cred); if (error) { put_filp(f); return ERR_PTR(error); } return f; is flat-out wrong. It used to be error = vfs_open(path, f, cred); if (!error) { /* from now on we need fput() to dispose of f */ error = open_check_o_direct(f); if (error) { fput(f); f = ERR_PTR(error); } } else { put_filp(f); f = ERR_PTR(error); } and sure, having that open_check_o_direct() boilerplate gotten rid of is nice, but not that way... Worse, another call chain (via finish_open()) is FUBAR now wrt FILE_OPENED handling - in that case we get error returned, with file already hit by fput() *AND* FILE_OPENED not set. Guess what happens in path_openat(), when it hits if (!(opened & FILE_OPENED)) { BUG_ON(!error); put_filp(file); } The root cause of all that crap is that the callers of do_dentry_open() have no way to tell which way did it fail; while that could be fixed up (by passing something like int *opened to do_dentry_open() and have it marked if we'd called ->open()), it's probably much too late in the cycle to do so right now. Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- fs/internal.h | 1 + fs/namei.c | 7 ++++++- fs/open.c | 44 ++++++++++++++++++++++++++------------------ 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index e08972db030366..980d005b21b411 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -125,6 +125,7 @@ int do_fchmodat(int dfd, const char __user *filename, umode_t mode); int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); +extern int open_check_o_direct(struct file *f); extern int vfs_open(const struct path *, struct file *, const struct cred *); extern struct file *filp_clone_open(struct file *); diff --git a/fs/namei.c b/fs/namei.c index 186bd2464fd5a8..4eb916996345d7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3367,7 +3367,9 @@ static int do_last(struct nameidata *nd, goto out; *opened |= FILE_OPENED; opened: - error = ima_file_check(file, op->acc_mode, *opened); + error = open_check_o_direct(file); + if (!error) + error = ima_file_check(file, op->acc_mode, *opened); if (!error && will_truncate) error = handle_truncate(file); out: @@ -3447,6 +3449,9 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags, error = finish_open(file, child, NULL, opened); if (error) goto out2; + error = open_check_o_direct(file); + if (error) + fput(file); out2: mnt_drop_write(path.mnt); out: diff --git a/fs/open.c b/fs/open.c index c5ee7cd6042409..d0e955b558ad84 100644 --- a/fs/open.c +++ b/fs/open.c @@ -724,6 +724,16 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group) return ksys_fchown(fd, user, group); } +int open_check_o_direct(struct file *f) +{ + /* NB: we're sure to have correct a_ops only after f_op->open */ + if (f->f_flags & O_DIRECT) { + if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO) + return -EINVAL; + } + return 0; +} + static int do_dentry_open(struct file *f, struct inode *inode, int (*open)(struct inode *, struct file *), @@ -745,7 +755,7 @@ static int do_dentry_open(struct file *f, if (unlikely(f->f_flags & O_PATH)) { f->f_mode = FMODE_PATH; f->f_op = &empty_fops; - goto done; + return 0; } if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) { @@ -798,12 +808,7 @@ static int do_dentry_open(struct file *f, f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping); -done: - /* NB: we're sure to have correct a_ops only after f_op->open */ - error = -EINVAL; - if ((f->f_flags & O_DIRECT) && - (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO)) - goto out_fput; + return 0; cleanup_all: @@ -818,9 +823,6 @@ static int do_dentry_open(struct file *f, f->f_path.dentry = NULL; f->f_inode = NULL; return error; -out_fput: - fput(f); - return error; } /** @@ -918,14 +920,20 @@ struct file *dentry_open(const struct path *path, int flags, BUG_ON(!path->mnt); f = get_empty_filp(); - if (IS_ERR(f)) - return f; - - f->f_flags = flags; - error = vfs_open(path, f, cred); - if (error) { - put_filp(f); - return ERR_PTR(error); + if (!IS_ERR(f)) { + f->f_flags = flags; + error = vfs_open(path, f, cred); + if (!error) { + /* from now on we need fput() to dispose of f */ + error = open_check_o_direct(f); + if (error) { + fput(f); + f = ERR_PTR(error); + } + } else { + put_filp(f); + f = ERR_PTR(error); + } } return f; } From 29dcea88779c856c7dc92040a0c01233263101d4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 3 Jun 2018 14:15:21 -0700 Subject: [PATCH 082/285] Linux 4.17 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 56ba070dfa0902..554dcaddbce450 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 4 PATCHLEVEL = 17 SUBLEVEL = 0 -EXTRAVERSION = -rc7 +EXTRAVERSION = NAME = Merciless Moray # *DOCUMENTATION* From 02a513acbd1bbb6d7b519fc66f5523be70fc496a Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Wed, 4 Apr 2018 06:19:37 +0200 Subject: [PATCH 083/285] ASoC: topology: Fix bclk and fsync inversion in set_link_hw_format() The values of bclk and fsync are inverted WRT the codec. But the existing solution already works for Broadwell, see the alsa-lib config: `alsa-lib/src/conf/topology/broadwell/broadwell.conf` This commit provides the backwards-compatible solution to fix this misuse. Signed-off-by: Kirill Marinushkin Reviewed-by: Pierre-Louis Bossart Tested-by: Pan Xiuli Tested-by: Pierre-Louis Bossart Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Mark Brown Cc: Liam Girdwood Cc: linux-kernel@vger.kernel.org Cc: alsa-devel@alsa-project.org Signed-off-by: Mark Brown (cherry picked from commit a941e2fab3207cb0d57dc4ec47b1b12c8ea78b84) --- include/uapi/sound/asoc.h | 16 ++++++++++++++-- sound/soc/soc-topology.c | 12 +++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 69c37ecbff7ee3..f0e5e21efa540d 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -160,6 +160,18 @@ #define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2) #define SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP (1 << 3) +/* DAI topology BCLK parameter + * For the backwards capability, by default codec is bclk master + */ +#define SND_SOC_TPLG_BCLK_CM 0 /* codec is bclk master */ +#define SND_SOC_TPLG_BCLK_CS 1 /* codec is bclk slave */ + +/* DAI topology FSYNC parameter + * For the backwards capability, by default codec is fsync master + */ +#define SND_SOC_TPLG_FSYNC_CM 0 /* codec is fsync master */ +#define SND_SOC_TPLG_FSYNC_CS 1 /* codec is fsync slave */ + /* * Block Header. * This header precedes all object and object arrays below. @@ -315,8 +327,8 @@ struct snd_soc_tplg_hw_config { __u8 clock_gated; /* 1 if clock can be gated to save power */ __u8 invert_bclk; /* 1 for inverted BCLK, 0 for normal */ __u8 invert_fsync; /* 1 for inverted frame clock, 0 for normal */ - __u8 bclk_master; /* 1 for master of BCLK, 0 for slave */ - __u8 fsync_master; /* 1 for master of FSYNC, 0 for slave */ + __u8 bclk_master; /* SND_SOC_TPLG_BCLK_ value */ + __u8 fsync_master; /* SND_SOC_TPLG_FSYNC_ value */ __u8 mclk_direction; /* 0 for input, 1 for output */ __le16 reserved; /* for 32bit alignment */ __le32 mclk_rate; /* MCLK or SYSCLK freqency in Hz */ diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 986b8b2f90fba5..82db57868d4fea 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2019,13 +2019,15 @@ static void set_link_hw_format(struct snd_soc_dai_link *link, link->dai_fmt |= SND_SOC_DAIFMT_IB_IF; /* clock masters */ - bclk_master = hw_config->bclk_master; - fsync_master = hw_config->fsync_master; - if (!bclk_master && !fsync_master) + bclk_master = (hw_config->bclk_master == + SND_SOC_TPLG_BCLK_CM); + fsync_master = (hw_config->fsync_master == + SND_SOC_TPLG_FSYNC_CM); + if (bclk_master && fsync_master) link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - else if (bclk_master && !fsync_master) - link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; else if (!bclk_master && fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + else if (bclk_master && !fsync_master) link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; else link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; From cba633966cdba72289a56fb559cc0de1851ad3c6 Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Wed, 4 Apr 2018 06:19:38 +0200 Subject: [PATCH 084/285] ASoC: topology: Add missing clock gating parameter when parsing hw_configs Clock gating parameter is a part of `dai_fmt`. It is supported by `alsa-lib` when creating a topology binary file, but ignored by kernel when loading this topology file. After applying this commit, the clock gating parameter is not ignored any more. This solution is backwards compatible. The existing behaviour is not broken, because by default the parameter value is 0 and is ignored. snd_soc_tplg_hw_config.clock_gated = 0 => no effect snd_soc_tplg_hw_config.clock_gated = 1 => SND_SOC_DAIFMT_GATED snd_soc_tplg_hw_config.clock_gated = 2 => SND_SOC_DAIFMT_CONT For example, the following config, based on alsa-lib/src/conf/topology/broadwell/broadwell.conf, is now supported: ~~~~ SectionHWConfig."CodecHWConfig" { id "1" format "I2S" # physical audio format. pm_gate_clocks "true" # clock can be gated } SectionLink."Codec" { # used for binding to the physical link id "0" hw_configs [ "CodecHWConfig" ] default_hw_conf_id "1" } ~~~~ Signed-off-by: Kirill Marinushkin Reviewed-by: Pierre-Louis Bossart Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Mark Brown Cc: Pan Xiuli Cc: Liam Girdwood Cc: linux-kernel@vger.kernel.org Cc: alsa-devel@alsa-project.org Signed-off-by: Mark Brown (cherry picked from commit 933e1c4a667103c4d10ebdc9505a0a6abd8c3fbd) --- include/uapi/sound/asoc.h | 7 ++++++- sound/soc/soc-topology.c | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index f0e5e21efa540d..f3c4b46e39d8b0 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -139,6 +139,11 @@ #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS (1 << 1) #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2) +/* DAI clock gating */ +#define SND_SOC_TPLG_DAI_CLK_GATE_UNDEFINED 0 +#define SND_SOC_TPLG_DAI_CLK_GATE_GATED 1 +#define SND_SOC_TPLG_DAI_CLK_GATE_CONT 2 + /* DAI physical PCM data formats. * Add new formats to the end of the list. */ @@ -324,7 +329,7 @@ struct snd_soc_tplg_hw_config { __le32 size; /* in bytes of this structure */ __le32 id; /* unique ID - - used to match */ __le32 fmt; /* SND_SOC_DAI_FORMAT_ format value */ - __u8 clock_gated; /* 1 if clock can be gated to save power */ + __u8 clock_gated; /* SND_SOC_TPLG_DAI_CLK_GATE_ value */ __u8 invert_bclk; /* 1 for inverted BCLK, 0 for normal */ __u8 invert_fsync; /* 1 for inverted frame clock, 0 for normal */ __u8 bclk_master; /* SND_SOC_TPLG_BCLK_ value */ diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 82db57868d4fea..f1b4e30995130e 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2006,6 +2006,13 @@ static void set_link_hw_format(struct snd_soc_dai_link *link, link->dai_fmt = hw_config->fmt & SND_SOC_DAIFMT_FORMAT_MASK; + /* clock gating */ + if (hw_config->clock_gated == SND_SOC_TPLG_DAI_CLK_GATE_GATED) + link->dai_fmt |= SND_SOC_DAIFMT_GATED; + else if (hw_config->clock_gated == + SND_SOC_TPLG_DAI_CLK_GATE_CONT) + link->dai_fmt |= SND_SOC_DAIFMT_CONT; + /* clock signal polarity */ invert_bclk = hw_config->invert_bclk; invert_fsync = hw_config->invert_fsync; From fdab57554be01e096ce8ded2a9d162710d5fd1cf Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Wed, 4 Apr 2018 06:19:39 +0200 Subject: [PATCH 085/285] ASoC: topology: Add definitions for mclk_direction values Current comment makes not clear the direction of mclk. Previously, similar description caused a misunderstanding for bclk_master and fsync_master. This commit solves the potential confusion the same way it is solved for bclk_master and fsync_master. Signed-off-by: Kirill Marinushkin Acked-by: Pierre-Louis Bossart Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Mark Brown Cc: Pan Xiuli Cc: Liam Girdwood Cc: linux-kernel@vger.kernel.org Cc: alsa-devel@alsa-project.org Signed-off-by: Mark Brown (cherry picked from commit e590522a06adce8ca2eb47e77d80616cd1542d91) --- include/uapi/sound/asoc.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index f3c4b46e39d8b0..b901cdbe532a07 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -144,6 +144,10 @@ #define SND_SOC_TPLG_DAI_CLK_GATE_GATED 1 #define SND_SOC_TPLG_DAI_CLK_GATE_CONT 2 +/* DAI mclk_direction */ +#define SND_SOC_TPLG_MCLK_CO 0 /* for codec, mclk is output */ +#define SND_SOC_TPLG_MCLK_CI 1 /* for codec, mclk is input */ + /* DAI physical PCM data formats. * Add new formats to the end of the list. */ @@ -334,7 +338,7 @@ struct snd_soc_tplg_hw_config { __u8 invert_fsync; /* 1 for inverted frame clock, 0 for normal */ __u8 bclk_master; /* SND_SOC_TPLG_BCLK_ value */ __u8 fsync_master; /* SND_SOC_TPLG_FSYNC_ value */ - __u8 mclk_direction; /* 0 for input, 1 for output */ + __u8 mclk_direction; /* SND_SOC_TPLG_MCLK_ value */ __le16 reserved; /* for 32bit alignment */ __le32 mclk_rate; /* MCLK or SYSCLK freqency in Hz */ __le32 bclk_rate; /* BCLK freqency in Hz */ From 5625100d83602bab12e67174de5d46bfcf69ea54 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 27 Mar 2018 14:30:45 +0100 Subject: [PATCH 086/285] ASoC: topology: Add support for compressed PCMs Register a compressed PCM if topology defines one. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit 5db6aab6f36f7560dc95f7ca340d5632b7a3be6a) --- sound/soc/soc-topology.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index f1b4e30995130e..8097ddbf67730b 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1754,6 +1754,9 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, set_stream_info(stream, caps); } + if (pcm->compress) + dai_drv->compress_new = snd_soc_new_compress; + /* pass control to component driver for optional further init */ ret = soc_tplg_dai_load(tplg, dai_drv); if (ret < 0) { From 64859fb736fc325398a8146e19583b5c5863253a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 29 May 2018 18:30:02 -0500 Subject: [PATCH 087/285] ASoC: fix 0-day warnings with snd_soc_new_compress() All conditionally-defined routines in include/sound/soc.h expose a static inline fallback to avoid 0-day warnings and compilation issues, except snd_soc_new_compress(). Fixes: 5db6aab6f36f ('ASoC: topology: Add support for compressed PCMs') Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 0b014d72ebae14c0c6ab3fb36a442fda91e1a1b3) --- include/sound/soc.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/sound/soc.h b/include/sound/soc.h index ad266d7e95537c..fc2e83e7caa24f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -492,6 +492,11 @@ int snd_soc_platform_write(struct snd_soc_platform *platform, int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); #ifdef CONFIG_SND_SOC_COMPRESS int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num); +#else +static inline int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) +{ + return 0; +} #endif void snd_soc_disconnect_sync(struct device *dev); From e022fa0892d833084450c686421c7b1c196f4e94 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 27 Apr 2018 16:36:00 -0500 Subject: [PATCH 088/285] ASoC: Intel: cht-bsw-rt5672: allow for topology-defined codec-dai setup Hard-coded setups conflict with topology defined ones. Move this code to codec_fixup so that SOF can override codec dai settings, e.g. to only use 2 channels. Signed-off-by: Pierre-Louis Bossart Acked-by: Vinod Koul Signed-off-by: Mark Brown (cherry picked from commit bf14adcc4ddd101088237179bee6daa19bac1669) --- sound/soc/intel/boards/cht_bsw_rt5672.c | 30 +++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index e68ec32720a4ad..e5aa13058dd738 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -189,13 +189,6 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) if (devm_acpi_dev_add_driver_gpios(component->dev, cht_rt5672_gpios)) dev_warn(runtime->dev, "Unable to add GPIO mapping table\n"); - /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); - if (ret < 0) { - dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); - return ret; - } - /* Select codec ASRC clock source to track I2S1 clock, because codec * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot * be supported by RT5672. Otherwise, ASRC will be disabled and cause @@ -252,6 +245,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int ret; /* The DSP will covert the FE rate to 48k, stereo, 24bits */ rate->min = rate->max = 48000; @@ -259,6 +253,26 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP2 to 24-bit */ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot + */ + ret = snd_soc_dai_set_fmt(rtd->codec_dai, + SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to TDM %d\n", ret); + return ret; + } + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(rtd->codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret); + return ret; + } + return 0; } @@ -315,8 +329,6 @@ static struct snd_soc_dai_link cht_dailink[] = { .nonatomic = true, .codec_dai_name = "rt5670-aif1", .codec_name = "i2c-10EC5670:00", - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBS_CFS, .init = cht_codec_init, .be_hw_params_fixup = cht_codec_fixup, .dpcm_playback = 1, From 6bde10d618f4bbf257d5164b7bd2db09ba38cec2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 9 May 2018 17:53:04 -0700 Subject: [PATCH 089/285] ALSA: core api: define offsets for TLV items Currently, there are no pre-defined accessors for the elements in topology TLV data. In the absence of such offsets, the tlv data will have to be decoded using hardwired offset numbers 0-N depending on the type of TLV. This patch defines accessor offsets for the type, length, min and mute/step items in TLV data for DB_SCALE type tlv's. These will be used by drivers to decode the TLV data while loading topology thereby improving code readability. The type and len offsets are common for all TLV types. The min and step/mute offsets are specific to DB_SCALE tlv type. Signed-off-by: Ranjani Sridharan Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai (cherry picked from commit 08f9f4485f2158de0fa77506687a073cb869e803) --- include/uapi/sound/tlv.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/uapi/sound/tlv.h b/include/uapi/sound/tlv.h index be5371f09a626b..e3437e96519a8a 100644 --- a/include/uapi/sound/tlv.h +++ b/include/uapi/sound/tlv.h @@ -42,6 +42,10 @@ #define SNDRV_CTL_TLVD_LENGTH(...) \ ((unsigned int)sizeof((const unsigned int[]) { __VA_ARGS__ })) +/* Accessor offsets for TLV data items */ +#define SNDRV_CTL_TLVO_TYPE 0 +#define SNDRV_CTL_TLVO_LEN 1 + #define SNDRV_CTL_TLVD_CONTAINER_ITEM(...) \ SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_CONTAINER, __VA_ARGS__) #define SNDRV_CTL_TLVD_DECLARE_CONTAINER(name, ...) \ @@ -61,6 +65,10 @@ SNDRV_CTL_TLVD_DB_SCALE_ITEM(min, step, mute) \ } +/* Accessor offsets for min, mute and step items in dB scale type TLV */ +#define SNDRV_CTL_TLVO_DB_SCALE_MIN 2 +#define SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP 3 + /* dB scale specified with min/max values instead of step */ #define SNDRV_CTL_TLVD_DB_MINMAX_ITEM(min_dB, max_dB) \ SNDRV_CTL_TLVD_ITEM(SNDRV_CTL_TLVT_DB_MINMAX, (min_dB), (max_dB)) From 4c685a7d8e3b62d364f0d678e9b1fb9a7bafe913 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 May 2018 17:53:26 -0500 Subject: [PATCH 090/285] ASoC: pcm512x: Add ACPI support HID is assumed to be made of TI PCI ID (0x104C) + part number, so all four 104C5121, 104C5122, 104C5141 104C5142 are valid. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit b84f48d18124da49a06e5d4ba6525b2955f15899) --- sound/soc/codecs/pcm512x-i2c.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 5f9c069569d5a9..0fe5ced841a383 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "pcm512x.h" @@ -52,6 +53,7 @@ static const struct i2c_device_id pcm512x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); +#if defined(CONFIG_OF) static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5121", }, { .compatible = "ti,pcm5122", }, @@ -60,6 +62,18 @@ static const struct of_device_id pcm512x_of_match[] = { { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id pcm512x_acpi_match[] = { + { "104C5121", 0 }, + { "104C5122", 0 }, + { "104C5141", 0 }, + { "104C5142", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match); +#endif static struct i2c_driver pcm512x_i2c_driver = { .probe = pcm512x_i2c_probe, @@ -67,7 +81,8 @@ static struct i2c_driver pcm512x_i2c_driver = { .id_table = pcm512x_i2c_id, .driver = { .name = "pcm512x", - .of_match_table = pcm512x_of_match, + .of_match_table = of_match_ptr(pcm512x_of_match), + .acpi_match_table = ACPI_PTR(pcm512x_acpi_match), .pm = &pcm512x_pm_ops, }, }; From 9f2ec80d821a8d54a62ab9ab16ce6c44a5513997 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 13 Apr 2018 14:45:37 +0200 Subject: [PATCH 091/285] ASoC: Intel: Disable SND_SOC_INTEL_BAYTRAIL when SND_SST_ATOM_HIFI2_PLATFORM is enabled The sound/soc/intel/common/sst-acpi.c code only tries to load the "baytrail-pcm-audio" driver (and supporting board drivers) when SND_SST_ATOM_HIFI2_PLATFORM is not enabled, since otherwise these are handled by snd-soc-sst-atom-hifi2-platform.ko. Since these thus will never be used when SND_SST_ATOM_HIFI2_PLATFORM is enabled, building these drivers when it is enabled is useless. Add a Kconfig dependency to reflect this, so that SND_SOC_INTEL_BAYTRAIL cannot be enabled when SND_SST_ATOM_HIFI2_PLATFORM is also enabled. Signed-off-by: Hans de Goede Acked-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit ed55fe24d7cb6fdc391ab808f22f163bd28928be) --- sound/soc/intel/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index addac2a8e52a57..0caa1f4eb94d7d 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -61,7 +61,7 @@ config SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI + depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE From 28fe6c4fddc935576bcb962238a665b53884c350 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 20:50:37 +0100 Subject: [PATCH 092/285] ASoC: topology: Give more data to clients via callbacks Give topology clients more access to the topology data by passing index, pcm, link_config and dai_driver to clients. This allows clients to fully instantiate and track topology objects. The SOF driver is the first user of these new APIs and needs them to build component topology driver and FW objects. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit c60b613a7097cff20fdd05e2891ce69542f0d5a3) --- include/sound/soc-topology.h | 23 +++++++++++-------- sound/soc/intel/skylake/skl-pcm.c | 7 +++--- sound/soc/intel/skylake/skl-topology.c | 5 +++-- sound/soc/intel/skylake/skl-topology.h | 5 +++-- sound/soc/soc-topology.c | 31 +++++++++++++++----------- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index f552c3f56368c4..e1f265e21ee128 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -30,6 +30,8 @@ struct snd_soc_dapm_context; struct snd_soc_card; struct snd_kcontrol_new; struct snd_soc_dai_link; +struct snd_soc_dai_driver; +struct snd_soc_dai; /* object scan be loaded and unloaded in groups with identfying indexes */ #define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */ @@ -109,35 +111,38 @@ struct snd_soc_tplg_widget_events { struct snd_soc_tplg_ops { /* external kcontrol init - used for any driver specific init */ - int (*control_load)(struct snd_soc_component *, + int (*control_load)(struct snd_soc_component *, int index, struct snd_kcontrol_new *, struct snd_soc_tplg_ctl_hdr *); int (*control_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* external widget init - used for any driver specific init */ - int (*widget_load)(struct snd_soc_component *, + int (*widget_load)(struct snd_soc_component *, int index, struct snd_soc_dapm_widget *, struct snd_soc_tplg_dapm_widget *); - int (*widget_ready)(struct snd_soc_component *, + int (*widget_ready)(struct snd_soc_component *, int index, struct snd_soc_dapm_widget *, struct snd_soc_tplg_dapm_widget *); int (*widget_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* FE DAI - used for any driver specific init */ - int (*dai_load)(struct snd_soc_component *, - struct snd_soc_dai_driver *dai_drv); + int (*dai_load)(struct snd_soc_component *, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); + int (*dai_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* DAI link - used for any driver specific init */ - int (*link_load)(struct snd_soc_component *, - struct snd_soc_dai_link *link); + int (*link_load)(struct snd_soc_component *, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg); int (*link_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* callback to handle vendor bespoke data */ - int (*vendor_load)(struct snd_soc_component *, + int (*vendor_load)(struct snd_soc_component *, int index, struct snd_soc_tplg_hdr *); int (*vendor_unload)(struct snd_soc_component *, struct snd_soc_tplg_hdr *); @@ -146,7 +151,7 @@ struct snd_soc_tplg_ops { void (*complete)(struct snd_soc_component *); /* manifest - optional to inform component of manifest */ - int (*manifest)(struct snd_soc_component *, + int (*manifest)(struct snd_soc_component *, int index, struct snd_soc_tplg_manifest *); /* vendor specific kcontrol handlers available for binding */ diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 15cb8ac3e374b8..c466265a2fbbc5 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -989,10 +989,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }; -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai) +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - pcm_dai->ops = &skl_pcm_dai_ops; + dai_drv->ops = &skl_pcm_dai_ops; return 0; } diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 3b1dca419883b9..6ac081f1f215df 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2851,7 +2851,7 @@ void skl_cleanup_resources(struct skl *skl) * information to the driver about module and pipeline parameters which DSP * FW expects like ids, resource values, formats etc */ -static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { @@ -2958,6 +2958,7 @@ static int skl_init_enum_data(struct device *dev, struct soc_enum *se, } static int skl_tplg_control_load(struct snd_soc_component *cmpnt, + int index, struct snd_kcontrol_new *kctl, struct snd_soc_tplg_ctl_hdr *hdr) { @@ -3446,7 +3447,7 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, return 0; } -static int skl_manifest_load(struct snd_soc_component *cmpnt, +static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_tplg_manifest *manifest) { struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b1e0667c0ae0a7..8fcba4cfca6cfc 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -512,8 +512,9 @@ int skl_pcm_host_dma_prepare(struct device *dev, int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai); +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); void skl_tplg_add_moduleid_in_bind_params(struct skl *skl, struct snd_soc_dapm_widget *w); #endif diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 8097ddbf67730b..f1c27a7be7ca8f 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -315,7 +315,7 @@ static int soc_tplg_vendor_load_(struct soc_tplg *tplg, int ret = 0; if (tplg->comp && tplg->ops && tplg->ops->vendor_load) - ret = tplg->ops->vendor_load(tplg->comp, hdr); + ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr); else { dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", hdr->vendor_type); @@ -347,7 +347,8 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_load) - return tplg->ops->widget_load(tplg->comp, w, tplg_w); + return tplg->ops->widget_load(tplg->comp, tplg->index, w, + tplg_w); return 0; } @@ -358,27 +359,30 @@ static int soc_tplg_widget_ready(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_ready) - return tplg->ops->widget_ready(tplg->comp, w, tplg_w); + return tplg->ops->widget_ready(tplg->comp, tplg->index, w, + tplg_w); return 0; } /* pass DAI configurations to component driver for extra initialization */ static int soc_tplg_dai_load(struct soc_tplg *tplg, - struct snd_soc_dai_driver *dai_drv) + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { if (tplg->comp && tplg->ops && tplg->ops->dai_load) - return tplg->ops->dai_load(tplg->comp, dai_drv); + return tplg->ops->dai_load(tplg->comp, tplg->index, dai_drv, + pcm, dai); return 0; } /* pass link configurations to component driver for extra initialization */ static int soc_tplg_dai_link_load(struct soc_tplg *tplg, - struct snd_soc_dai_link *link) + struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { if (tplg->comp && tplg->ops && tplg->ops->link_load) - return tplg->ops->link_load(tplg->comp, link); + return tplg->ops->link_load(tplg->comp, tplg->index, link, cfg); return 0; } @@ -699,7 +703,8 @@ static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) { if (tplg->comp && tplg->ops && tplg->ops->control_load) - return tplg->ops->control_load(tplg->comp, k, hdr); + return tplg->ops->control_load(tplg->comp, tplg->index, k, + hdr); return 0; } @@ -1758,7 +1763,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, dai_drv->compress_new = snd_soc_new_compress; /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); kfree(dai_drv); @@ -1828,7 +1833,7 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, set_link_flags(link, pcm->flag_mask, pcm->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); kfree(link); @@ -2128,7 +2133,7 @@ static int soc_tplg_link_config(struct soc_tplg *tplg, set_link_flags(link, cfg->flag_mask, cfg->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, cfg); if (ret < 0) { dev_err(tplg->dev, "ASoC: physical link loading failed\n"); return ret; @@ -2250,7 +2255,7 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg, set_dai_flags(dai_drv, d->flag_mask, d->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); return ret; @@ -2356,7 +2361,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, /* pass control to component driver for optional further init */ if (tplg->comp && tplg->ops && tplg->ops->manifest) - return tplg->ops->manifest(tplg->comp, _manifest); + return tplg->ops->manifest(tplg->comp, tplg->index, _manifest); if (!abi_match) /* free the duplicated one */ kfree(_manifest); From 4c8ac45f0f150b4e3ec46afd5aa9cb2bf9b163d1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 20:53:59 +0100 Subject: [PATCH 093/285] ASoC: topology: Add callback for DAPM route load/unload Add a callback fro clients for notification about DAPM route loading and unloading. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit 503e79b793fea5de626db73accf8e8994bc4289d) --- include/sound/soc-topology.h | 7 +++++++ sound/soc/soc-topology.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index e1f265e21ee128..401ef2c45d6c76 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -32,6 +32,7 @@ struct snd_kcontrol_new; struct snd_soc_dai_link; struct snd_soc_dai_driver; struct snd_soc_dai; +struct snd_soc_dapm_route; /* object scan be loaded and unloaded in groups with identfying indexes */ #define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */ @@ -116,6 +117,12 @@ struct snd_soc_tplg_ops { int (*control_unload)(struct snd_soc_component *, struct snd_soc_dobj *); + /* DAPM graph route element loading and unloading */ + int (*dapm_route_load)(struct snd_soc_component *, int index, + struct snd_soc_dapm_route *route); + int (*dapm_route_unload)(struct snd_soc_component *, + struct snd_soc_dobj *); + /* external widget init - used for any driver specific init */ int (*widget_load)(struct snd_soc_component *, int index, struct snd_soc_dapm_widget *, diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index f1c27a7be7ca8f..1dfd2782225a58 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1161,6 +1161,17 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, return 0; } +/* optionally pass new dynamic kcontrol to component driver. */ +static int soc_tplg_add_route(struct soc_tplg *tplg, + struct snd_soc_dapm_route *route) +{ + if (tplg->comp && tplg->ops && tplg->ops->dapm_route_load) + return tplg->ops->dapm_route_load(tplg->comp, tplg->index, + route); + + return 0; +} + static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { @@ -1209,6 +1220,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, else route.control = elem->control; + soc_tplg_add_route(tplg, &route); + /* add route, but keep going if some fail */ snd_soc_dapm_add_routes(dapm, &route, 1); } From 2695cf512c5c5971828f39d36e192740e34def8a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 20:26:42 +0100 Subject: [PATCH 094/285] ASoC: dapm: Fix potential DAI widget pointer deref when linking DAIs Sometime a component or topology may configure a DAI widget with no private data leading to a dev_dbg() dereferencne of this data. Fix this to check for non NULL private data and let users know if widget is missing DAI. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown (cherry picked from commit e01b4f624278d5efe5fb5da585ca371947b16680) --- sound/soc/soc-dapm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2d9709104ec55e..6d54128e44b46b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4075,6 +4075,13 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) continue; } + /* let users know there is no DAI to link */ + if (!dai_w->priv) { + dev_dbg(card->dev, "dai widget %s has no DAI\n", + dai_w->name); + continue; + } + dai = dai_w->priv; /* ...find all widgets with the same stream and link them */ From 02dd6a5758675f833ff9b9183586301355766e98 Mon Sep 17 00:00:00 2001 From: Naveen Manohar Date: Mon, 18 Jun 2018 13:29:35 -0500 Subject: [PATCH 095/285] ASoC: Intel: broxton: reduce machine name for bxt_da7219_max98357a Use truncated names in bxt id table and bxt_da7219_max98357a machine as platform device id table expects names to be less then 20chars. Signed-off-by: Naveen Manohar Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 95555f580dca21fac5ea35c10fa92fa034bd403f) --- sound/soc/intel/boards/bxt_da7219_max98357a.c | 4 ++-- sound/soc/intel/skylake/skl.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 668c0934e942ba..ce021a3efc534d 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -586,7 +586,7 @@ static int broxton_audio_probe(struct platform_device *pdev) static struct platform_driver broxton_audio = { .probe = broxton_audio_probe, .driver = { - .name = "bxt_da7219_max98357a_i2s", + .name = "bxt_da7219_max98357a", .pm = &snd_soc_pm_ops, }, }; @@ -599,4 +599,4 @@ MODULE_AUTHOR("Rohit Ainapure "); MODULE_AUTHOR("Harsha Priya "); MODULE_AUTHOR("Conrad Cooke "); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s"); +MODULE_ALIAS("platform:bxt_da7219_max98357a"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index abf324747b2901..d50c2eed46deaa 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1086,7 +1086,7 @@ static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { }, { .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a_i2s", + .drv_name = "bxt_da7219_max98357a", .fw_filename = "intel/dsp_fw_bxtn.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, From 3c71f58ad503db20420dbec734e18da014dc23a0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:36 -0500 Subject: [PATCH 096/285] ASoC: Intel: Skylake: cleanup before moving ACPI tables There is no need to deal with DMICs if the DSP is not present and there is no ACPI machine ID found. Simplify before moving these ACPI tables to sound/soc/intel/common Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 5f15f267daf81a4c7c2a1cd2a0d6743ec7fc8b59) --- sound/soc/intel/skylake/skl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index d50c2eed46deaa..f1692178666d3b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -493,10 +493,12 @@ static int skl_find_machine(struct skl *skl, void *driver_data) skl->mach = mach; skl->fw_name = mach->fw_filename; - pdata = skl->mach->pdata; + pdata = mach->pdata; - if (mach->pdata) + if (pdata) { skl->use_tplg_pcm = pdata->use_tplg_pcm; + pdata->dmic_num = skl_get_dmic_geo(skl); + } return 0; } @@ -923,8 +925,6 @@ static int skl_probe(struct pci_dev *pci, pci_set_drvdata(skl->pci, ebus); - skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); - /* check if dsp is there */ if (bus->ppcap) { /* create device for dsp clk */ From 7a3d05442197e5d9a5a2471ebb539282565444ea Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:37 -0500 Subject: [PATCH 097/285] ASoC: Intel: move SKL+ codec ACPI tables to common directory No functionality change, just move to common tables to make it easier to deal with SOF and share the same machine drivers - as done previously for BYT/CHT/HSW/BDW. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit cbaa7f0bdbee1969bb311c641abbd0d2af6ba861) --- include/sound/soc-acpi-intel-match.h | 5 + sound/soc/intel/common/Makefile | 6 +- .../intel/common/soc-acpi-intel-bxt-match.c | 35 ++++ .../intel/common/soc-acpi-intel-cnl-match.c | 29 ++++ .../intel/common/soc-acpi-intel-glk-match.c | 23 +++ .../intel/common/soc-acpi-intel-kbl-match.c | 91 ++++++++++ .../intel/common/soc-acpi-intel-skl-match.c | 47 +++++ sound/soc/intel/skylake/skl.c | 162 +----------------- 8 files changed, 241 insertions(+), 157 deletions(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-bxt-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-cnl-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-glk-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-kbl-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-skl-match.c diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 9da6388c20a1cc..917ddd0f2762ea 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -29,5 +29,10 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; #endif diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 7379d8830c391e..915a34cdc8acbb 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -3,7 +3,11 @@ snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o -snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o soc-acpi-intel-hsw-bdw-match.o +snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o \ + soc-acpi-intel-hsw-bdw-match.o \ + soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ + soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ + soc-acpi-intel-cnl-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c new file mode 100644 index 00000000000000..569f1de97e82da --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-bxt-match.c - tables and support for BXT ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include + +static struct snd_soc_acpi_codecs bxt_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, + { + .id = "DLGS7219", + .drv_name = "bxt_da7219_max98357a", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &bxt_codecs, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c new file mode 100644 index 00000000000000..b83ee053d41767 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-cnl-match.c - tables and support for CNL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata cnl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { + { + .id = "INT34C2", + .drv_name = "cnl_rt274", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c new file mode 100644 index 00000000000000..dee09439e7cb16 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-glk-match.c - tables and support for GLK ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include + +struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { + { + .id = "INT343A", + .drv_name = "glk_alc298s_i2s", + .fw_filename = "intel/dsp_fw_glk.bin", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c new file mode 100644 index 00000000000000..0ee173ca437dd3 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-kbl-match.c - tables and support for KBL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs kbl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { + .num_codecs = 1, + .codecs = {"10EC5663"} +}; + +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { + .num_codecs = 2, + .codecs = {"10EC5663", "10EC5514"} +}; + +static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { + { + .id = "INT343A", + .drv_name = "kbl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "INT343B", + .drv_name = "kbl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "kbl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_r5514_5663_max", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_5663_5514_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_rt5663_m98927", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_poppy_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "10EC5663", + .drv_name = "kbl_rt5663", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "DLGS7219", + .drv_name = "kbl_da7219_max98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_7219_98357_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-skl-match.c b/sound/soc/intel/common/soc-acpi-intel-skl-match.c new file mode 100644 index 00000000000000..0c9c0edd35b34d --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-skl-match.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-skl-match.c - tables and support for SKL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs skl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[] = { + { + .id = "INT343A", + .drv_name = "skl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_release.bin", + }, + { + .id = "INT343B", + .drv_name = "skl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "skl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_skl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f1692178666d3b..8def7cc58586e7 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,8 +37,6 @@ #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" -static struct skl_machine_pdata skl_dmic_data; - /* * initialize the PCI registers */ @@ -1019,172 +1018,23 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct snd_soc_acpi_codecs skl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs kbl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs bxt_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct snd_soc_acpi_codecs kbl_poppy_codecs = { - .num_codecs = 1, - .codecs = {"10EC5663"} -}; - -static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { - .num_codecs = 2, - .codecs = {"10EC5663", "10EC5514"} -}; - -static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct skl_machine_pdata cnl_pdata = { - .use_tplg_pcm = true, -}; - -static struct snd_soc_acpi_mach sst_skl_devdata[] = { - { - .id = "INT343A", - .drv_name = "skl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_release.bin", - }, - { - .id = "INT343B", - .drv_name = "skl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "skl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - {} -}; - -static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { - { - .id = "INT343A", - .drv_name = "bxt_alc298s_i2s", - .fw_filename = "intel/dsp_fw_bxtn.bin", - }, - { - .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a", - .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &bxt_codecs, - }, - {} -}; - -static struct snd_soc_acpi_mach sst_kbl_devdata[] = { - { - .id = "INT343A", - .drv_name = "kbl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "INT343B", - .drv_name = "kbl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "kbl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_r5514_5663_max", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_5663_5514_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_rt5663_m98927", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_poppy_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "10EC5663", - .drv_name = "kbl_rt5663", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "DLGS7219", - .drv_name = "kbl_da7219_max98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_7219_98357_codecs, - .pdata = &skl_dmic_data - }, - - {} -}; - -static struct snd_soc_acpi_mach sst_glk_devdata[] = { - { - .id = "INT343A", - .drv_name = "glk_alc298s_i2s", - .fw_filename = "intel/dsp_fw_glk.bin", - }, - {} -}; - -static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { - { - .id = "INT34C2", - .drv_name = "cnl_rt274", - .fw_filename = "intel/dsp_fw_cnl.bin", - .pdata = &cnl_pdata, - }, - {} -}; - /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = (unsigned long)&sst_skl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_skl_machines}, /* BXT-P */ { PCI_DEVICE(0x8086, 0x5a98), - .driver_data = (unsigned long)&sst_bxtp_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_bxt_machines}, /* KBL */ { PCI_DEVICE(0x8086, 0x9D71), - .driver_data = (unsigned long)&sst_kbl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_kbl_machines}, /* GLK */ { PCI_DEVICE(0x8086, 0x3198), - .driver_data = (unsigned long)&sst_glk_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_glk_machines}, /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), - .driver_data = (unsigned long)&sst_cnl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); From 674ee8abd9bf7615dbc070f1923f5f5d534096c1 Mon Sep 17 00:00:00 2001 From: Naveen Manohar Date: Mon, 18 Jun 2018 13:29:38 -0500 Subject: [PATCH 098/285] ASoC: Intel: common: Add Geminilake Dialog+Maxim machine driver entry This patch adds da7219_max98357a machine driver entry into machine table Signed-off-by: Naveen Manohar Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit 65a33883c778befcb85ef45285763fd8ac1b2ba3) --- sound/soc/intel/common/soc-acpi-intel-glk-match.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index dee09439e7cb16..5902aa1d0ee38f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -9,12 +9,24 @@ #include #include +static struct snd_soc_acpi_codecs glk_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { { .id = "INT343A", .drv_name = "glk_alc298s_i2s", .fw_filename = "intel/dsp_fw_glk.bin", }, + { + .id = "DLGS7219", + .drv_name = "glk_da7219_max98357a", + .fw_filename = "intel/dsp_fw_glk.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &glk_codecs, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); From 19d4dd5df40f2f76c2ec4b81930957159bda06f4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:39 -0500 Subject: [PATCH 099/285] ASoC: Intel: common: add firmware/topology information for SOF No functionality change for Skylake driver, add relevant names needed by SOF for BXT/APL, GLK and CNL. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit e6d298fd4a4454dd121343323e3f00a27f8819a4) --- sound/soc/intel/common/soc-acpi-intel-bxt-match.c | 3 +++ sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 3 +++ sound/soc/intel/common/soc-acpi-intel-glk-match.c | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 569f1de97e82da..50869eddbb3b00 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -26,6 +26,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .fw_filename = "intel/dsp_fw_bxtn.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index b83ee053d41767..ec8e28e7b937aa 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -20,6 +20,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .drv_name = "cnl_rt274", .fw_filename = "intel/dsp_fw_cnl.bin", .pdata = &cnl_pdata, + .sof_fw_filename = "intel/sof-cnl.ri", + .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index 5902aa1d0ee38f..305875af71caee 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -19,6 +19,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .id = "INT343A", .drv_name = "glk_alc298s_i2s", .fw_filename = "intel/dsp_fw_glk.bin", + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-alc298.tplg", + .asoc_plat_name = "0000:00:0e.0", }, { .id = "DLGS7219", @@ -26,6 +29,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .fw_filename = "intel/dsp_fw_glk.bin", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &glk_codecs, + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", }, {}, }; From 82e7ce8b7e6b30bef30316be335524449cc25962 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:41 -0500 Subject: [PATCH 100/285] ASoC: Intel: common: add entries for SOF-based machine drivers While we are at it, add entries for machine drivers that are used on SOF-based platforms. The drivers will be submitted upstream after the core SOF patches, but there's no harm in adding these references now. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit b45350135b9241b64cc91ccc8dddca2ee4dc25d7) --- .../intel/common/soc-acpi-intel-bxt-match.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 50869eddbb3b00..f39386e540d322 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -30,6 +30,27 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .sof_tplg_filename = "intel/sof-apl-da7219.tplg", .asoc_plat_name = "0000:00:0e.0", }, + { + .id = "104C5122", + .drv_name = "bxt-pcm512x", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-pcm512x.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "1AEC8804", + .drv_name = "bxt-wm8804", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-wm8804.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "INT34C3", + .drv_name = "bxt_tdf8532", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); From c94ec7c2daa5fa0e6c37224a13b03632a010f7e7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 18 Jun 2018 13:29:42 -0500 Subject: [PATCH 101/285] ASoC: Intel: common: fix copy/paste issue with SOF/broadwell topology file There are two commercially-available Broadwell platforms based on I2S (Dell XPS13 and 'Samus' Pixel 2015 Chromebook). Fix a copy/paste issue to allow each platform to enable different features if needed when SOF is enabled Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown (cherry picked from commit f0d9034b290d8bad590e843c2a1081eb47d813ad) --- sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index e0e8c8c27528cb..53e7acdfbb81ac 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -45,7 +45,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_tplg_filename = "intel/reef-bdw-rt5677.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { From 453d69143b3cf295b23b99414aa2bbcd9bb9fa8c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 12 Mar 2018 15:35:11 -0500 Subject: [PATCH 102/285] ASoC: Intel: common: rename 'reef' to 'sof' in ACPI matching table Align with firmware tools, no functionality change Signed-off-by: Pierre-Louis Bossart --- .../intel/common/soc-acpi-intel-byt-match.c | 40 +++++++------- .../intel/common/soc-acpi-intel-cht-match.c | 52 +++++++++---------- .../common/soc-acpi-intel-hsw-bdw-match.c | 16 +++--- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index bfe1ca68a54234..4daa8a4f0c0c69 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -59,8 +59,8 @@ static struct snd_soc_acpi_mach byt_thinkpad_10 = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5670.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -98,8 +98,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", .machine_quirk = byt_quirk, - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -107,8 +107,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -116,8 +116,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -125,8 +125,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5651.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -134,8 +134,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -143,8 +143,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some Baytrail platforms rely on RT5645, use CHT machine driver */ @@ -153,8 +153,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -162,8 +162,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* use CHT driver to Baytrail Chromebooks */ @@ -172,8 +172,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-max98090.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index ad1eb2d644beab..3c3f2f8585d719 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -44,8 +44,8 @@ static struct snd_soc_acpi_mach cht_surface_mach = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -68,8 +68,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -77,8 +77,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -86,8 +86,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -95,8 +95,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -104,8 +104,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -113,8 +113,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-max98090.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -131,8 +131,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -140,8 +140,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -149,8 +149,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_es8316", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_es8316", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-es8316.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-es8316.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ @@ -160,8 +160,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", .machine_quirk = cht_quirk, - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -169,8 +169,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ @@ -179,8 +179,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5651.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index 53e7acdfbb81ac..494a0ea9b02903 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -23,8 +23,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST1.bin", - .sof_fw_filename = "intel/reef-hsw.ri", - .sof_tplg_filename = "intel/reef-hsw.tplg", + .sof_fw_filename = "intel/sof-hsw.ri", + .sof_tplg_filename = "intel/sof-hsw.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} @@ -36,24 +36,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .id = "INT343A", .drv_name = "broadwell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt286.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "RT5677CE", .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt5677.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5677.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt5640.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5640.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} From f4cd2bce71d35dfc9959f998047fa2bb3ea570ed Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 14:54:53 +0000 Subject: [PATCH 103/285] ASoC: core: Allow topology to override machine driver FE DAI link config. Machine drivers statically define a number of DAI links that currently cannot be changed or removed by topology. This means PCMs and platform components cannot be changed by topology at runtime AND machine drivers are tightly coupled to topology. This patch allows topology to override the machine driver DAI link config in order to reuse machine drivers with different topologies and platform components. The patch supports :- 1) create new FE PCMs with a topology defined PCM ID. 2) destroy existing static FE PCMs 3) change the platform component driver. 4) assign any new HW params fixups. The patch requires no changes to the machine drivers, but does add some platform component flags that the platform component driver can assign before loading topologies. Signed-off-by: Liam Girdwood --- include/sound/soc.h | 10 ++++++ sound/soc/soc-core.c | 79 ++++++++++++++++++++++++++++++++++++++++++-- sound/soc/soc-pcm.c | 12 +++++++ 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index fc2e83e7caa24f..4da57378c3cb72 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1017,6 +1017,13 @@ struct snd_soc_platform_driver { /* platform stream compress ops */ const struct snd_compr_ops *compr_ops; + + /* this platform uses topology and ignore machine driver FEs */ + const char *ignore_machine; + int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); + bool use_dai_pcm_id; /* use the DAI link PCM ID as PCM device number */ + int be_pcm_base; /* base device ID for all BE PCMs */ }; struct snd_soc_dai_link_component { @@ -1123,6 +1130,9 @@ struct snd_soc_dai_link { /* pmdown_time is ignored at stop */ unsigned int ignore_pmdown_time:1; + /* Do not create a PCM for this DAI link (Backend link) */ + unsigned int ignore:1; + struct list_head list; /* DAI link list of the soc card */ struct snd_soc_dobj dobj; /* For topology */ }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bf7ca32ab31fca..efbe4eea1e8a98 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1050,6 +1050,9 @@ static int soc_bind_dai_link(struct snd_soc_card *card, const char *platform_name; int i; + if (dai_link->ignore) + return 0; + dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); if (soc_is_dai_link_bound(card, dai_link)) { @@ -1672,7 +1675,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, { struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret; + int i, ret, num; dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", card->name, rtd->num, order); @@ -1718,9 +1721,23 @@ static int soc_probe_link_dais(struct snd_soc_card *card, soc_dpcm_debugfs_add(rtd); #endif + /* + * most drivers will register their PCMs using DAI link ordering but + * topology based drivers can use the DAI link id field to set PCM + * device number and then use rtd + a base offset of the BEs. + */ + if (rtd->platform->driver->use_dai_pcm_id) { + if (rtd->dai_link->no_pcm) + num = rtd->platform->driver->be_pcm_base + rtd->num; + else + num = rtd->dai_link->id; + } else { + num = rtd->num; + } + if (cpu_dai->driver->compress_new) { /*create compress_device"*/ - ret = cpu_dai->driver->compress_new(rtd, rtd->num); + ret = cpu_dai->driver->compress_new(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); @@ -1730,7 +1747,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, if (!dai_link->params) { /* create the pcm */ - ret = soc_new_pcm(rtd, rtd->num); + ret = soc_new_pcm(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", dai_link->stream_name, ret); @@ -2076,6 +2093,59 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); #endif /* CONFIG_DMI */ +static void soc_check_tplg_fes(struct snd_soc_card *card) +{ + struct snd_soc_platform *platform; + struct snd_soc_dai_link *dai_link; + int i; + + list_for_each_entry(platform, &platform_list, list) { + + /* does this platform override FEs ? */ + if (!platform->driver->ignore_machine) + continue; + + /* for this machine ? */ + if (strcmp(platform->driver->ignore_machine, + card->dev->driver->name)) + continue; + + /* machine matches, so override the rtd data */ + for (i = 0; i < card->num_links; i++) { + + dai_link = &card->dai_link[i]; + + /* ignore this FE */ + if (dai_link->dynamic) { + dai_link->ignore = true; + continue; + } + + dev_info(card->dev, "info: override FE DAI link %s\n", + card->dai_link[i].name); + + /* override platform */ + dai_link->platform_name = platform->component.name; + dai_link->cpu_dai_name = platform->component.name; + + /* convert non BE into BE */ + dai_link->no_pcm = 1; + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + + /* override any BE fixups */ + dai_link->be_hw_params_fixup = + platform->driver->be_hw_params_fixup; + + /* most BE links don't set stream name, so set it to + * dai link name if it's NULL to help bind widgets. + */ + if (!dai_link->stream_name) + dai_link->stream_name = dai_link->name; + } + } +} + static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_codec *codec; @@ -2086,6 +2156,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) mutex_lock(&client_mutex); mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); + /* check whether any platform is ignore machine FE and using topology */ + soc_check_tplg_fes(card); + /* bind DAIs */ for (i = 0; i < card->num_links; i++) { ret = soc_bind_dai_link(card, &card->dai_link[i]); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 68d9dc93009636..4ce489165a6d9b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -909,8 +909,20 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; int ret; + /* perform any topology hw_params fixups before DAI */ + if (rtd->dai_link->be_hw_params_fixup) { + ret = rtd->dai_link->be_hw_params_fixup(rtd, params); + if (ret < 0) { + dev_err(rtd->dev, + "ASoC: hw_params topology fixup failed %d\n", + ret); + return ret; + } + } + if (dai->driver->ops->hw_params) { ret = dai->driver->ops->hw_params(substream, params, dai); if (ret < 0) { From ca845055f78216acad6400e0f5e30b6ab5e09c8e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 8 Mar 2018 18:23:38 -0600 Subject: [PATCH 104/285] [SQUASHME?] ASoC: core: Add name prefix for machines with topology rewrites Signed-off-by: Liam Girdwood --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/include/sound/soc.h b/include/sound/soc.h index 4da57378c3cb72..649dd7d3187581 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1020,6 +1020,7 @@ struct snd_soc_platform_driver { /* this platform uses topology and ignore machine driver FEs */ const char *ignore_machine; + const char *topology_name_prefix; int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); bool use_dai_pcm_id; /* use the DAI link PCM ID as PCM device number */ @@ -1172,6 +1173,7 @@ struct snd_soc_card { const char *long_name; const char *driver_name; char dmi_longname[80]; + char topology_shortname[32]; struct device *dev; struct snd_card *snd_card; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index efbe4eea1e8a98..a0160a409e4094 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2143,6 +2143,14 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) if (!dai_link->stream_name) dai_link->stream_name = dai_link->name; } + + /* Inform userspace we are using alternate topology */ + if (platform->driver->topology_name_prefix) { + snprintf(card->topology_shortname, 32, "%s-%s", + platform->driver->topology_name_prefix, + card->name); + card->name = card->topology_shortname; + } } } From 51f05df2e5e9cf7832a22044f95dfd70cbef8c35 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:15:35 +0000 Subject: [PATCH 105/285] ALSA: core: Allow drivers to set R/W wait time. Currently ALSA core blocks userspace for about 10 seconds for PCM R/W IO. This needs to be configurable for modern hardware like DSPs where no pointer update in milliseconds can indicate terminal DSP errors. Add a substream variable to set the wait time in ms. This allows userspace and drivers to recover more quickly from terminal DSP errors. Signed-off-by: Liam Girdwood --- include/sound/pcm.h | 6 ++++++ sound/core/pcm_lib.c | 15 ++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index e054c583d3b339..e4694684c524d1 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -462,6 +462,7 @@ struct snd_pcm_substream { /* -- timer section -- */ struct snd_timer *timer; /* timer */ unsigned timer_running: 1; /* time is running */ + unsigned wait_time; /* time in ms for R/W to wait for avail */ /* -- next substream -- */ struct snd_pcm_substream *next; /* -- linked substreams -- */ @@ -579,6 +580,11 @@ int snd_pcm_start(struct snd_pcm_substream *substream); int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status); int snd_pcm_drain_done(struct snd_pcm_substream *substream); int snd_pcm_stop_xrun(struct snd_pcm_substream *substream); +static inline void snd_pcm_wait_time(struct snd_pcm_substream *substream, + unsigned wait_time) +{ + substream->wait_time = wait_time; +} #ifdef CONFIG_PM int snd_pcm_suspend(struct snd_pcm_substream *substream); int snd_pcm_suspend_all(struct snd_pcm *pcm); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index f4a19509cccf3c..6b8e48d48e111b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1835,12 +1835,17 @@ static int wait_for_avail(struct snd_pcm_substream *substream, if (runtime->no_period_wakeup) wait_time = MAX_SCHEDULE_TIMEOUT; else { - wait_time = 10; - if (runtime->rate) { - long t = runtime->period_size * 2 / runtime->rate; - wait_time = max(t, wait_time); + /* use wait time from substream if available */ + if (substream->wait_time) { + wait_time = msecs_to_jiffies(substream->wait_time); + } else { + wait_time = 10; + if (runtime->rate) { + long t = runtime->period_size * 2 / runtime->rate; + wait_time = max(t, wait_time); + } + wait_time = msecs_to_jiffies(wait_time * 1000); } - wait_time = msecs_to_jiffies(wait_time * 1000); } for (;;) { From d4da47509021f16d726dc6a5df21bc5f7912bc26 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:30:59 +0000 Subject: [PATCH 106/285] ASoC: SOF: Add Sound Open Firmware driver core The Sound Open Firmware driver core is a generic architecture independent layer that allows SOF to be used on many different different architectures and platforms. It abstracts DSP operations and IO methods so that the target DSP can be an internal memory mapped or external SPI or I2C based device. This abstraction also allows SOF to be run on many different VMs on the same physical HW. SOF also requires some data in ASoC PCM runtime data for looking up SOF data during ASoC PCM operations. Signed-off-by: Liam Girdwood --- include/sound/soc.h | 1 + include/sound/sof.h | 76 +++++++ sound/soc/sof/core.c | 422 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 436 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 935 insertions(+) create mode 100644 include/sound/sof.h create mode 100644 sound/soc/sof/core.c create mode 100644 sound/soc/sof/sof-priv.h diff --git a/include/sound/soc.h b/include/sound/soc.h index 649dd7d3187581..71a745fa8131d4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1293,6 +1293,7 @@ struct snd_soc_pcm_runtime { /* runtime devices */ struct snd_pcm *pcm; struct snd_compr *compr; + struct snd_sof_pcm *sof; struct snd_soc_codec *codec; struct snd_soc_platform *platform; /* will be removed */ struct snd_soc_dai *codec_dai; diff --git a/include/sound/sof.h b/include/sound/sof.h new file mode 100644 index 00000000000000..7767ec8ce188d6 --- /dev/null +++ b/include/sound/sof.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __INCLUDE_SOUND_SOF_H +#define __INCLUDE_SOUND_SOF_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct snd_sof_dsp_ops; + +/* + * SOF Platform data. + */ +struct snd_sof_pdata { + u32 id; /* PCI/ACPI ID */ + const struct firmware *fw; + const char *drv_name; + const char *name; + + /* parent devices */ + struct device *dev; + struct pci_dev *pci; + struct platform_device *pdev; + + /* descriptor */ + const struct sof_dev_desc *desc; + + /* machine */ + struct platform_device *pdev_mach; + const struct snd_soc_acpi_mach *machine; +}; + +/* + * Descriptor used for setting up SOF platform data. This is used when + * ACPI/PCI data is missing or mapped differently. + */ +struct sof_dev_desc { + /* list of machines using this configuration */ + struct snd_soc_acpi_mach *machines; + + /* Platform resource indexes in BAR / ACPI resources. */ + /* Must set to -1 if not used - add new items to end */ + int resindex_lpe_base; + int resindex_pcicfg_base; + int resindex_imr_base; + int irqindex_host_ipc; + int resindex_dma_base; + + /* DMA only valid when resindex_dma_base != -1*/ + int dma_engine; + int dma_size; + + /* IPC timeouts in ms */ + int ipc_timeout; + int boot_timeout; + + /* defaults for no codec mode */ + const char *nocodec_fw_filename; + const char *nocodec_tplg_filename; +}; + +#endif diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c new file mode 100644 index 00000000000000..e15c393cd57476 --- /dev/null +++ b/sound/soc/sof/core.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +#define TIMEOUT_IPC 5 +#define TIMEOUT_BOOT 100 + +struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->pcm.dai_id == rtd->dai_link->id) + return spcm; + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, + char *name) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (strcmp(spcm->pcm.dai_name, name) == 0) + return spcm; + + if (strcmp(spcm->pcm.caps[0].name, name) == 0) + return spcm; + + if (strcmp(spcm->pcm.caps[1].name, name) == 0) + return spcm; + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id == + comp_id) { + *direction = SNDRV_PCM_STREAM_PLAYBACK; + return spcm; + } + if (spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id == comp_id) { + *direction = SNDRV_PCM_STREAM_CAPTURE; + return spcm; + } + } + + return NULL; +} + +struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, + unsigned int pcm_id) +{ + struct snd_sof_pcm *spcm = NULL; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->pcm.pcm_id == pcm_id) + return spcm; + } + + return NULL; +} + +struct snd_sof_widget *snd_sof_find_swidget(struct snd_sof_dev *sdev, + char *name) +{ + struct snd_sof_widget *swidget = NULL; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (strcmp(name, swidget->widget->name) == 0) + return swidget; + } + + return NULL; +} + +struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, + char *name) +{ + struct snd_sof_dai *dai = NULL; + + list_for_each_entry(dai, &sdev->dai_list, list) { + if (!dai->name) + continue; + + if (strcmp(name, dai->name) == 0) + return dai; + } + + return NULL; +} + +static inline unsigned int sof_get_pages(size_t size) +{ + return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; +} + +struct sof_panic_msg { + u32 id; + const char *msg; +}; + +static const struct sof_panic_msg panic_msg[] = { + {SOF_IPC_PANIC_MEM, "out of memory"}, + {SOF_IPC_PANIC_WORK, "work subsystem init failed"}, + {SOF_IPC_PANIC_IPC, "IPC subsystem init failed"}, + {SOF_IPC_PANIC_ARCH, "arch init failed"}, + {SOF_IPC_PANIC_PLATFORM, "platform init failed"}, + {SOF_IPC_PANIC_TASK, "scheduler init failed"}, + {SOF_IPC_PANIC_EXCEPTION, "runtime exception"}, + {SOF_IPC_PANIC_DEADLOCK, "deadlock"}, + {SOF_IPC_PANIC_STACK, "stack overflow"}, + {SOF_IPC_PANIC_IDLE, "can't enter idle"}, +}; + +/* only need xtensa atm */ +static void sof_arch_dsp_oops(struct snd_sof_dev *sdev, void *oops) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + + dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", + xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); + dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", + xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); + dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", + xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); + dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", + xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); + dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", + xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); +} + +static void sof_dsp_dump_stack(struct snd_sof_dev *sdev, void *oops, + u32 *stack, u32 stack_words) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + u32 stack_ptr = xoops->stack; + int i; + + dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); + + for (i = 0; i <= stack_words - 4; i += 4) { + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + i, stack[i], stack[i + 1], stack[i + 2], + stack[i + 3]); + } + + /* deal with any remaining words */ + switch (stack_words - i) { + case 0: + break; + case 1: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x\n", + stack_ptr + stack_words - 1, stack[stack_words - 1]); + break; + case 2: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 2, stack[stack_words - 2], + stack[stack_words - 1]); + break; + case 3: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 3, stack[stack_words - 3], + stack[stack_words - 2], stack[stack_words - 1]); + break; + default: + break; + } +} + +int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, + u32 tracep_code, void *oops, void *stack, + size_t stack_words) +{ + u32 code; + int i; + + /* is firmware dead ? */ + if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) { + dev_err(sdev->dev, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n", + panic_code, tracep_code); + return 0; /* no fault ? */ + } + + code = panic_code & + (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK); + + for (i = 0; i < ARRAY_SIZE(panic_msg); i++) { + if (panic_msg[i].id == code) { + dev_err(sdev->dev, "error: %s\n", panic_msg[i].msg); + dev_err(sdev->dev, "error: trace point %8.8x\n", + tracep_code); + goto out; + } + } + + /* unknown error */ + dev_err(sdev->dev, "error: unknown reason %8.8x\n", panic_code); + dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); + +out: + sof_arch_dsp_oops(sdev, oops); + sof_dsp_dump_stack(sdev, oops, stack, stack_words); + return -EFAULT; +} +EXPORT_SYMBOL(snd_sof_get_status); + +static int sof_probe(struct platform_device *pdev) +{ + struct snd_sof_pdata *plat_data = dev_get_platdata(&pdev->dev); + struct snd_sof_dev *sdev; + int ret; + + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return -ENOMEM; + + dev_dbg(&pdev->dev, "probing SOF DSP device....\n"); + + /* initialize sof device */ + sdev->dev = &pdev->dev; + if (plat_data->pci) { + sdev->pci = plat_data->pci; + sdev->parent = &plat_data->pci->dev; + } else if (plat_data->pdev) { + sdev->parent = &plat_data->pdev->dev; + } else { + sdev->parent = plat_data->dev; + } + sdev->ops = plat_data->machine->pdata; + + sdev->pdata = plat_data; + INIT_LIST_HEAD(&sdev->pcm_list); + INIT_LIST_HEAD(&sdev->kcontrol_list); + INIT_LIST_HEAD(&sdev->widget_list); + INIT_LIST_HEAD(&sdev->dai_list); + dev_set_drvdata(&pdev->dev, sdev); + spin_lock_init(&sdev->ipc_lock); + spin_lock_init(&sdev->hw_lock); + + /* set up platform and component drivers */ + snd_sof_new_platform_drv(sdev); + snd_sof_new_dai_drv(sdev); + + /* set default timeouts if none provided */ + if (plat_data->desc->ipc_timeout == 0) + sdev->ipc_timeout = TIMEOUT_IPC; + else + sdev->ipc_timeout = plat_data->desc->ipc_timeout; + if (plat_data->desc->boot_timeout == 0) + sdev->boot_timeout = TIMEOUT_BOOT; + else + sdev->boot_timeout = plat_data->desc->boot_timeout; + + /* probe the DSP hardware */ + ret = snd_sof_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret); + return ret; + } + + /* register any debug/trace capabilities */ + ret = snd_sof_dbg_init(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n", + ret); + goto dbg_err; + } + + /* init the IPC */ + sdev->ipc = snd_sof_ipc_init(sdev); + if (!sdev->ipc) { + dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret); + goto ipc_err; + } + + /* load the firmware */ + ret = snd_sof_load_firmware(sdev, plat_data->fw); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", + ret); + goto fw_load_err; + } + + /* boot the firmware */ + ret = snd_sof_run_firmware(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", + ret); + goto fw_run_err; + } + + /* now register audio DSP platform driver */ + ret = snd_soc_register_platform(&pdev->dev, &sdev->plat_drv); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to register DSP platform driver %d\n", + ret); + goto fw_run_err; + } + + ret = snd_soc_register_component(&pdev->dev, sdev->cmpnt_drv, + &sdev->dai_drv, sdev->num_dai); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to register DSP DAI driver %d\n", ret); + goto comp_err; + } + + ret = snd_sof_init_trace(sdev); + if (ret < 0) { + dev_warn(sdev->dev, + "warning: failed to initialize trace %d\n", ret); + } + + return 0; + +comp_err: + snd_soc_unregister_component(&pdev->dev); + snd_sof_free_topology(sdev); +fw_run_err: + snd_sof_fw_unload(sdev); +fw_load_err: + snd_sof_ipc_free(sdev); +ipc_err: + snd_sof_free_debug(sdev); +dbg_err: + snd_sof_remove(sdev); + + return ret; +} + +static int sof_remove(struct platform_device *pdev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_platform(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + snd_sof_fw_unload(sdev); + snd_sof_ipc_free(sdev); + snd_sof_free_debug(sdev); + snd_sof_release_trace(sdev); + snd_sof_remove(sdev); + return 0; +} + +void snd_sof_shutdown(struct device *dev) +{ +} +EXPORT_SYMBOL(snd_sof_shutdown); + +int snd_sof_create_page_table(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size) +{ + int i, pages; + + pages = sof_get_pages(size); + + dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n", + dmab->area, size, pages); + + for (i = 0; i < pages; i++) { + u32 idx = (((i << 2) + i)) >> 1; + u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; + u32 *pg_table; + + dev_dbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + + pg_table = (u32 *)(page_table + idx); + + if (i & 1) + *pg_table |= (pfn << 4); + else + *pg_table |= pfn; + } + + return pages; +} + +static struct platform_driver sof_driver = { + .driver = { + .name = "sof-audio", + }, + + .probe = sof_probe, + .remove = sof_remove, +}; +module_platform_driver(sof_driver); + +MODULE_AUTHOR("Liam Girdwood"); +MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:sof-audio"); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h new file mode 100644 index 00000000000000..0f9b659461d94e --- /dev/null +++ b/sound/soc/sof/sof-priv.h @@ -0,0 +1,436 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOUND_SOC_SOF_PRIV_H +#define __SOUND_SOC_SOF_PRIV_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* debug flags */ +#define SOF_DBG_REGS BIT(1) +#define SOF_DBG_MBOX BIT(2) +#define SOF_DBG_TEXT BIT(3) +#define SOF_DBG_PCI BIT(4) + +/* max BARs mmaped devices can use */ +#define SND_SOF_BARS 8 + +/* time in ms for runtime suspend delay */ +#define SND_SOF_SUSPEND_DELAY 2000 + +/* DMA buffer size for trace */ +#define DMA_BUF_SIZE_FOR_TRACE (PAGE_SIZE * 16) + +/* max number of FE PCMs before BEs */ +#define SOF_BE_PCM_BASE 16 + +struct snd_sof_dev; +struct snd_sof_ipc_msg; +struct snd_sof_ipc; +struct snd_sof_debugfs_map; +struct snd_soc_tplg_ops; +struct snd_soc_component; + +struct snd_sof_dsp_ops { + /* probe and remove */ + int (*remove)(struct snd_sof_dev *sof_dev); + int (*probe)(struct snd_sof_dev *sof_dev); + + /* DSP core boot / reset */ + int (*run)(struct snd_sof_dev *sof_dev); + int (*stall)(struct snd_sof_dev *sof_dev); + int (*reset)(struct snd_sof_dev *sof_dev); + + /* DSP PM */ + int (*suspend)(struct snd_sof_dev *sof_dev, int state); + int (*resume)(struct snd_sof_dev *sof_dev); + + /* DSP clocking */ + int (*set_clk)(struct snd_sof_dev *sof_dev, u32 freq); + + /* Register IO */ + void (*write)(struct snd_sof_dev *sof_dev, void __iomem *addr, + u32 value); + u32 (*read)(struct snd_sof_dev *sof_dev, void __iomem *addr); + void (*write64)(struct snd_sof_dev *sof_dev, void __iomem *addr, + u64 value); + u64 (*read64)(struct snd_sof_dev *sof_dev, void __iomem *addr); + + /* memcpy IO */ + void (*block_read)(struct snd_sof_dev *sof_dev, + u32 offset, void *dest, size_t size); + void (*block_write)(struct snd_sof_dev *sof_dev, + u32 offset, void *src, size_t size); + + /* doorbell */ + irqreturn_t (*irq_handler)(int irq, void *context); + irqreturn_t (*irq_thread)(int irq, void *context); + + /* mailbox */ + void (*mailbox_read)(struct snd_sof_dev *sof_dev, u32 offset, + void __iomem *addr, size_t bytes); + void (*mailbox_write)(struct snd_sof_dev *sof_dev, u32 offset, + void __iomem *addr, size_t bytes); + + /* ipc */ + int (*send_msg)(struct snd_sof_dev *sof_dev, + struct snd_sof_ipc_msg *msg); + int (*get_reply)(struct snd_sof_dev *sof_dev, + struct snd_sof_ipc_msg *msg); + int (*is_ready)(struct snd_sof_dev *sof_dev); + int (*cmd_done)(struct snd_sof_dev *sof_dev); + + /* debug */ + const struct snd_sof_debugfs_map *debug_map; + int debug_map_count; + void (*dbg_dump)(struct snd_sof_dev *sof_dev, u32 flags); + + /* connect pcm substream to a host stream */ + int (*host_stream_open)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); + /* disconnect pcm substream to a host stream */ + int (*host_stream_close)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); + + /* host stream hw params */ + int (*host_stream_hw_params)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); + + /* host stream trigger */ + int (*host_stream_trigger)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + int cmd); + + /* FW loading */ + int (*load_firmware)(struct snd_sof_dev *sof_dev, + const struct firmware *fw); + int (*load_module)(struct snd_sof_dev *sof_dev, + struct snd_sof_mod_hdr *hdr); + int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); + + /* host DMA trace initialization */ + int (*trace_init)(struct snd_sof_dev *sdev, u32 *stream_tag); + int (*trace_release)(struct snd_sof_dev *sdev); + int (*trace_trigger)(struct snd_sof_dev *sdev, int cmd); +}; + +struct snd_sof_pdata; + +struct sof_ops_table { + const struct sof_dev_desc *desc; + struct snd_sof_dsp_ops *ops; + struct platform_device *(*new_data)(struct snd_sof_pdata *pdata); +}; + +struct snd_sof_dfsentry { + struct dentry *dfsentry; + size_t size; + void *buf; + struct snd_sof_dev *sdev; +}; + +struct snd_sof_debugfs_map { + const char *name; + u32 bar; + u32 offset; + u32 size; +}; + +struct snd_sof_mailbox { + u32 offset; + size_t size; +}; + +struct snd_sof_pcm_stream { + u32 comp_id; + struct snd_dma_buffer page_table; + struct sof_ipc_stream_posn posn; + struct snd_pcm_substream *substream; +}; + +struct snd_sof_pcm { + struct snd_sof_dev *sdev; + struct snd_soc_tplg_pcm pcm; + struct snd_sof_pcm_stream stream[2]; + u32 posn_offset[2]; + struct mutex mutex; + struct list_head list; /* list in sdev pcm list */ +}; + +struct snd_sof_control { + struct snd_sof_dev *sdev; + int comp_id; + int num_channels; + u32 readback_offset; /* offset to mmaped data if used */ + struct sof_ipc_ctrl_data *control_data; + u32 size; /* cdata size */ + enum sof_ipc_ctrl_cmd cmd; + + struct mutex mutex; + struct list_head list; /* list in sdev control list */ +}; + +struct snd_sof_widget { + struct snd_sof_dev *sdev; + int comp_id; + int pipeline_id; + int complete; + int id; + + struct snd_soc_dapm_widget *widget; + struct mutex mutex; + struct list_head list; /* list in sdev widget list */ + + void *private; /* core does not touch this */ +}; + +struct snd_sof_dai { + struct snd_sof_dev *sdev; + const char *name; + + struct sof_ipc_comp_dai comp_dai; + struct sof_ipc_dai_config dai_config; + struct list_head list; /* list in sdev dai list */ +}; + +struct snd_sof_ipc_msg { + struct list_head list; + + /* message data */ + u32 header; + void *msg_data; + void *reply_data; + size_t msg_size; + size_t reply_size; + + wait_queue_head_t waitq; + bool complete; +}; + +struct sof_intel_hda_dev; + +/* + * SOF Device Level. + */ +struct snd_sof_dev { + struct device *dev; + struct device *parent; + spinlock_t ipc_lock; /* lock for IPC users */ + spinlock_t hw_lock; /* lock for HW IO access */ + struct pci_dev *pci; + + /* ASoC components */ + struct snd_soc_platform_driver plat_drv; + const struct snd_soc_component_driver *cmpnt_drv; + struct snd_soc_dai_driver dai_drv; + int num_dai; + + /* DSP firmware boot */ + wait_queue_head_t boot_wait; + bool boot_complete; + + /* DSP HW differentiation */ + struct snd_sof_pdata *pdata; + const struct snd_sof_dsp_ops *ops; + struct sof_intel_hda_dev *hda; /* for HDA based DSP HW */ + + /* IPC */ + struct snd_sof_ipc *ipc; + struct snd_sof_mailbox dsp_box; /* DSP initiated IPC */ + struct snd_sof_mailbox host_box; /* Host initiated IPC */ + struct snd_sof_mailbox stream_box; /* Stream position update */ + u64 irq_status; + int ipc_irq; + u32 next_comp_id; /* monotonic - reset during S3 */ + + /* memory bases for mmaped DSPs - set by dsp_init() */ + void __iomem *bar[SND_SOF_BARS]; /* DSP base address */ + int mmio_bar; + int mailbox_bar; + size_t dsp_oops_offset; + + /* debug */ + struct dentry *debugfs_root; + + /* firmware loader */ + int cl_bar; + struct snd_dma_buffer dmab; + struct sof_ipc_fw_ready fw_ready; + + /* topology */ + struct snd_soc_tplg_ops *tplg_ops; + struct list_head pcm_list; + struct list_head kcontrol_list; + struct list_head widget_list; + struct list_head dai_list; + struct snd_soc_component *component; + + /* FW configuration */ + struct sof_ipc_dma_buffer_data *info_buffer; + struct sof_ipc_window *info_window; + + /* IPC timeouts in ms */ + int ipc_timeout; + int boot_timeout; + + /* Wait queue for code loading */ + wait_queue_head_t waitq; + int code_loading; + + /* DMA for Trace */ + struct snd_dma_buffer dmatb; + struct snd_dma_buffer dmatp; + int dma_trace_pages; + wait_queue_head_t trace_sleep; + u32 host_offset; + bool dtrace_is_enabled; + + void *private; /* core does not touch this */ +}; + +/* + * Device Level. + */ +void snd_sof_shutdown(struct device *dev); +int snd_sof_runtime_suspend(struct device *dev); +int snd_sof_runtime_resume(struct device *dev); +int snd_sof_resume(struct device *dev); +int snd_sof_suspend(struct device *dev); +int snd_sof_suspend_late(struct device *dev); + +void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); +void snd_sof_new_dai_drv(struct snd_sof_dev *sdev); + +int snd_sof_create_page_table(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size); + +/* + * Firmware loading. + */ +int snd_sof_load_firmware(struct snd_sof_dev *sdev, + const struct firmware *fw); +int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, + const struct firmware *fw); +int snd_sof_run_firmware(struct snd_sof_dev *sdev); +int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, + struct snd_sof_mod_hdr *module); +void snd_sof_fw_unload(struct snd_sof_dev *sdev); +int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset); + +/* + * IPC low level APIs. + */ +struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev); +void snd_sof_ipc_free(struct snd_sof_dev *sdev); +void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); +void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev); +void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev); +int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev, + struct sof_ipc_pcm_params *params); +int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, + size_t dspbox_size, u32 hostbox, + size_t hostbox_size); +int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *tx_data, + size_t tx_bytes, void *rx_data, size_t rx_bytes); +struct snd_sof_widget *snd_sof_find_swidget(struct snd_sof_dev *sdev, + char *name); +struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, + char *name); +struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime *rtd); +struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, + char *name); +struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction); +struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, + unsigned int pcm_id); + +/* + * Stream IPC + */ +int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm, int direction, + struct sof_ipc_stream_posn *posn); + +/* + * Mixer IPC + */ +int snd_sof_ipc_set_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd); +int snd_sof_ipc_get_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd); + +/* + * Topology. + */ +int snd_sof_init_topology(struct snd_sof_dev *sdev, + struct snd_soc_tplg_ops *ops); +int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file); +void snd_sof_free_topology(struct snd_sof_dev *sdev); + +/* + * Trace/debug + */ +int snd_sof_init_trace(struct snd_sof_dev *sdev); +void snd_sof_release_trace(struct snd_sof_dev *sdev); +int snd_sof_dbg_init(struct snd_sof_dev *sdev); +void snd_sof_free_debug(struct snd_sof_dev *sdev); +int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, + void __iomem *base, size_t size, + const char *name); +int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn); +void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); +int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, + u32 tracep_code, void *oops, void *stack, + size_t stack_size); + +/* + * Platform specific ops. + */ +extern struct snd_compr_ops sof_compressed_ops; + +/* + * Kcontrols. + */ + +int snd_sof_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +#endif From d2ee6b199d6a5fdcba4e63ae4539162bac4bcf4a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:31:31 +0000 Subject: [PATCH 107/285] ASoC: SOF: Add Sound Open Firmware KControl support SOF exposes regular ALSA Kcontrols that are defined by topology. This patch converts the Kcontrol IO to DSP IPC. Signed-off-by: Liam Girdwood --- sound/soc/sof/control.c | 197 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 sound/soc/sof/control.c diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c new file mode 100644 index 00000000000000..db09ba63154b69 --- /dev/null +++ b/sound/soc/sof/control.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* Mixer Controls */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) +{ + if (value >= size) + return volume_map[size - 1]; + else + return volume_map[value]; +} + +static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (volume_map[i] >= value) + return i; + } + + return i - 1; +} + +int snd_sof_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *sm = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_sof_control *scontrol = sm->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_VOLUME); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = + ipc_to_mixer(cdata->chanv[i].value, + scontrol->volume_table, sm->max + 1); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *sm = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_sof_control *scontrol = sm->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* update each channel */ + for (i = 0; i < channels; i++) { + cdata->chanv[i].value = + mixer_to_ipc(ucontrol->value.integer.value[i], + scontrol->volume_table, sm->max + 1); + cdata->chanv[i].channel = i; + } + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_VOLUME); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *se = + (struct soc_enum *)kcontrol->private_value; + struct snd_sof_control *scontrol = se->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_ENUM); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = cdata->chanv[i].value; + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *se = + (struct soc_enum *)kcontrol->private_value; + struct snd_sof_control *scontrol = se->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* update each channel */ + for (i = 0; i < channels; i++) + cdata->chanv[i].value = ucontrol->value.integer.value[i]; + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_SET, + SOF_CTRL_CMD_ENUM); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *be = + (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + //struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + //unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_DATA, + SOF_CTRL_TYPE_DATA_GET, scontrol->cmd); + + /* TODO: copy back to userspace */ + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *be = + (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + //struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + //unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* TODO: copy from userspace */ + + /* notify DSP of mixer updates */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_SET_DATA, + SOF_CTRL_TYPE_DATA_SET, scontrol->cmd); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} From 4c649d0447a74d8ae4139f78054425a59874bca5 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:31:44 +0000 Subject: [PATCH 108/285] ASoC: SOF: Add driver debug support. Add debugFS files that can be used to expose DSP memories and and peripherals to userspace to assist with firmware debugging. Signed-off-by: Liam Girdwood --- sound/soc/sof/debug.c | 137 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 sound/soc/sof/debug.c diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c new file mode 100644 index 00000000000000..c1580a17d2f2d2 --- /dev/null +++ b/sound/soc/sof/debug.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Yan Wang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +static int sof_dfsentry_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + int size; + u32 *buf; + loff_t pos = *ppos; + size_t ret; + + size = dfse->size; + + if (pos < 0) + return -EINVAL; + if (pos >= size || !count) + return 0; + if (count > size - pos) + count = size - pos; + + size = (count + 3) & ~3; + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pm_runtime_get(sdev->dev); + memcpy_fromio(buf, dfse->buf + pos, size); + pm_runtime_put(sdev->dev); + + ret = copy_to_user(buffer, buf, count); + kfree(buf); + + if (ret == count) + return -EFAULT; + count -= ret; + *ppos = pos + count; + + return count; +} + +static const struct file_operations sof_dfs_fops = { + .open = sof_dfsentry_open, + .read = sof_dfsentry_read, + .llseek = default_llseek, +}; + +int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, + void __iomem *base, size_t size, + const char *name) +{ + struct snd_sof_dfsentry *dfse; + + if (!sdev) + return -EINVAL; + + dfse = kzalloc(sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->buf = base; + dfse->size = size; + dfse->sdev = sdev; + + dfse->dfsentry = debugfs_create_file(name, 0444, sdev->debugfs_root, + dfse, &sof_dfs_fops); + if (!dfse->dfsentry) { + dev_err(sdev->dev, "cannot create debugfs entry.\n"); + kfree(dfse); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_debugfs_create_item); + +int snd_sof_dbg_init(struct snd_sof_dev *sdev) +{ + const struct snd_sof_dsp_ops *ops = sdev->ops; + const struct snd_sof_debugfs_map *map; + int err = 0, i; + + sdev->debugfs_root = debugfs_create_dir("sof", NULL); + if (IS_ERR_OR_NULL(sdev->debugfs_root)) { + dev_err(sdev->dev, "error: failed to create debugfs directory\n"); + return -EINVAL; + } + + for (i = 0; i < ops->debug_map_count; i++) { + map = &ops->debug_map[i]; + + err = snd_sof_debugfs_create_item(sdev, sdev->bar[map->bar] + + map->offset, map->size, + map->name); + if (err < 0) + dev_err(sdev->dev, "cannot create debugfs for %s\n", + map->name); + } + + return err; +} +EXPORT_SYMBOL(snd_sof_dbg_init); + +void snd_sof_free_debug(struct snd_sof_dev *sdev) +{ + debugfs_remove_recursive(sdev->debugfs_root); +} +EXPORT_SYMBOL(snd_sof_free_debug); From ce4ffebccad797d12e7171da1933a79ecd64b5df Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:32:12 +0000 Subject: [PATCH 109/285] ASoC: SOF: Add support for IPC IO between DSP and Host Define an IPC ABI for all host <--> DSP communication. This ABI should be transport agnostic. i.e. it should work on MMIO and SPI/I2C style interfaces. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-ipc.h | 860 +++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc.c | 609 +++++++++++++++++++++++++ 2 files changed, 1469 insertions(+) create mode 100644 include/uapi/sound/sof-ipc.h create mode 100644 sound/soc/sof/ipc.c diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h new file mode 100644 index 00000000000000..9b33691ac8a94b --- /dev/null +++ b/include/uapi/sound/sof-ipc.h @@ -0,0 +1,860 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Keyon Jie + */ + +#ifndef __INCLUDE_UAPI_SOF_IPC_H__ +#define __INCLUDE_UAPI_SOF_IPC_H__ + +#include + +/* + * IPC messages have a prefixed 32 bit identifier made up as follows :- + * + * 0xGCCCNNNN where + * G is global cmd type (4 bits) + * C is command type (12 bits) + * I is the ID number (16 bits) - monotonic and overflows + * + * This is sent at the start of the IPM message in the mailbox. Messages should + * not be sent in the doorbell (special exceptions for firmware . + */ + +/* Global Message - Generic */ +#define SOF_GLB_TYPE_SHIFT 28 +#define SOF_GLB_TYPE_MASK (0xf << SOF_GLB_TYPE_SHIFT) +#define SOF_GLB_TYPE(x) ((x) << SOF_GLB_TYPE_SHIFT) + +/* Command Message - Generic */ +#define SOF_CMD_TYPE_SHIFT 16 +#define SOF_CMD_TYPE_MASK (0xfff << SOF_CMD_TYPE_SHIFT) +#define SOF_CMD_TYPE(x) ((x) << SOF_CMD_TYPE_SHIFT) + +/* Global Message Types */ +#define SOF_IPC_GLB_REPLY SOF_GLB_TYPE(0x1U) +#define SOF_IPC_GLB_COMPOUND SOF_GLB_TYPE(0x2U) +#define SOF_IPC_GLB_TPLG_MSG SOF_GLB_TYPE(0x3U) +#define SOF_IPC_GLB_PM_MSG SOF_GLB_TYPE(0x4U) +#define SOF_IPC_GLB_COMP_MSG SOF_GLB_TYPE(0x5U) +#define SOF_IPC_GLB_STREAM_MSG SOF_GLB_TYPE(0x6U) +#define SOF_IPC_FW_READY SOF_GLB_TYPE(0x7U) +#define SOF_IPC_GLB_DAI_MSG SOF_GLB_TYPE(0x8U) +#define SOF_IPC_GLB_TRACE_MSG SOF_GLB_TYPE(0x9U) + +/* + * DSP Command Message Types + */ + +/* topology */ +#define SOF_IPC_TPLG_COMP_NEW SOF_CMD_TYPE(0x001) +#define SOF_IPC_TPLG_COMP_FREE SOF_CMD_TYPE(0x002) +#define SOF_IPC_TPLG_COMP_CONNECT SOF_CMD_TYPE(0x003) +#define SOF_IPC_TPLG_PIPE_NEW SOF_CMD_TYPE(0x010) +#define SOF_IPC_TPLG_PIPE_FREE SOF_CMD_TYPE(0x011) +#define SOF_IPC_TPLG_PIPE_CONNECT SOF_CMD_TYPE(0x012) +#define SOF_IPC_TPLG_PIPE_COMPLETE SOF_CMD_TYPE(0x013) +#define SOF_IPC_TPLG_BUFFER_NEW SOF_CMD_TYPE(0x020) +#define SOF_IPC_TPLG_BUFFER_FREE SOF_CMD_TYPE(0x021) + +/* PM */ +#define SOF_IPC_PM_CTX_SAVE SOF_CMD_TYPE(0x001) +#define SOF_IPC_PM_CTX_RESTORE SOF_CMD_TYPE(0x002) +#define SOF_IPC_PM_CTX_SIZE SOF_CMD_TYPE(0x003) +#define SOF_IPC_PM_CLK_SET SOF_CMD_TYPE(0x004) +#define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005) +#define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006) + +/* component runtime config - multiple different types */ +#define SOF_IPC_COMP_SET_VALUE SOF_CMD_TYPE(0x001) +#define SOF_IPC_COMP_GET_VALUE SOF_CMD_TYPE(0x002) +#define SOF_IPC_COMP_SET_DATA SOF_CMD_TYPE(0x003) +#define SOF_IPC_COMP_GET_DATA SOF_CMD_TYPE(0x004) + +/* DAI messages */ +#define SOF_IPC_DAI_CONFIG SOF_CMD_TYPE(0x001) +#define SOF_IPC_DAI_LOOPBACK SOF_CMD_TYPE(0x002) + +/* stream */ +#define SOF_IPC_STREAM_PCM_PARAMS SOF_CMD_TYPE(0x001) +#define SOF_IPC_STREAM_PCM_PARAMS_REPLY SOF_CMD_TYPE(0x002) +#define SOF_IPC_STREAM_PCM_FREE SOF_CMD_TYPE(0x003) +#define SOF_IPC_STREAM_TRIG_START SOF_CMD_TYPE(0x004) +#define SOF_IPC_STREAM_TRIG_STOP SOF_CMD_TYPE(0x005) +#define SOF_IPC_STREAM_TRIG_PAUSE SOF_CMD_TYPE(0x006) +#define SOF_IPC_STREAM_TRIG_RELEASE SOF_CMD_TYPE(0x007) +#define SOF_IPC_STREAM_TRIG_DRAIN SOF_CMD_TYPE(0x008) +#define SOF_IPC_STREAM_TRIG_XRUN SOF_CMD_TYPE(0x009) +#define SOF_IPC_STREAM_POSITION SOF_CMD_TYPE(0x00a) +#define SOF_IPC_STREAM_VORBIS_PARAMS SOF_CMD_TYPE(0x010) +#define SOF_IPC_STREAM_VORBIS_FREE SOF_CMD_TYPE(0x011) + +/* trace and debug */ +#define SOF_IPC_TRACE_DMA_PARAMS SOF_CMD_TYPE(0x001) +#define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002) + +/* Get message component id */ +#define SOF_IPC_MESSAGE_ID(x) ((x) & 0xffff) + +/* maximum message size for mailbox Tx/Rx */ +#define SOF_IPC_MSG_MAX_SIZE 128 + +/* + * SOF panic codes + */ +#define SOF_IPC_PANIC_MAGIC 0x0dead000 +#define SOF_IPC_PANIC_MAGIC_MASK 0x0ffff000 +#define SOF_IPC_PANIC_CODE_MASK 0x00000fff +#define SOF_IPC_PANIC_MEM (SOF_IPC_PANIC_MAGIC | 0) +#define SOF_IPC_PANIC_WORK (SOF_IPC_PANIC_MAGIC | 1) +#define SOF_IPC_PANIC_IPC (SOF_IPC_PANIC_MAGIC | 2) +#define SOF_IPC_PANIC_ARCH (SOF_IPC_PANIC_MAGIC | 3) +#define SOF_IPC_PANIC_PLATFORM (SOF_IPC_PANIC_MAGIC | 4) +#define SOF_IPC_PANIC_TASK (SOF_IPC_PANIC_MAGIC | 5) +#define SOF_IPC_PANIC_EXCEPTION (SOF_IPC_PANIC_MAGIC | 6) +#define SOF_IPC_PANIC_DEADLOCK (SOF_IPC_PANIC_MAGIC | 7) +#define SOF_IPC_PANIC_STACK (SOF_IPC_PANIC_MAGIC | 8) +#define SOF_IPC_PANIC_IDLE (SOF_IPC_PANIC_MAGIC | 9) + +/* + * SOF memory capabilities, add new ones at the end + */ +#define SOF_MEM_CAPS_RAM (1 << 0) +#define SOF_MEM_CAPS_ROM (1 << 1) +#define SOF_MEM_CAPS_EXT (1 << 2) /* external */ +#define SOF_MEM_CAPS_LP (1 << 3) /* low power */ +#define SOF_MEM_CAPS_HP (1 << 4) /* high performance */ +#define SOF_MEM_CAPS_DMA (1 << 5) /* DMA'able */ +#define SOF_MEM_CAPS_CACHE (1 << 6) /* cacheable */ + +/* + * Command Header - Header for all IPC. Identifies IPC message. + * The size can be greater than the structure size and that means there is + * extended bespoke data beyond the end of the structure including variable + * arrays. + */ + +struct sof_ipc_hdr { + uint32_t cmd; /* SOF_IPC_GLB_ + cmd */ + uint32_t size; /* size of structure */ +} __attribute__((packed)); + +/* + * Generic reply message. Some commands override this with their own reply + * types that must include this at start. + */ +struct sof_ipc_reply { + struct sof_ipc_hdr hdr; + int32_t error; /* negative error numbers */ +} __attribute__((packed)); + +/* + * Compound commands - SOF_IPC_GLB_COMPOUND. + * + * Compound commands are sent to the DSP as a single IPC operation. The + * commands are split into blocks and each block has a header. This header + * identifies the command type and the number of commands before the next + * header. + */ + +struct sof_ipc_compound_hdr { + struct sof_ipc_hdr hdr; + uint32_t count; /* count of 0 means end of compound sequence */ +} __attribute__((packed)); + +/* + * DAI Configuration. + * + * Each different DAI type will have it's own structure and IPC cmd. + */ + +#define SOF_DAI_FMT_I2S 1 /* I2S mode */ +#define SOF_DAI_FMT_RIGHT_J 2 /* Right Justified mode */ +#define SOF_DAI_FMT_LEFT_J 3 /* Left Justified mode */ +#define SOF_DAI_FMT_DSP_A 4 /* L data MSB after FRM LRC */ +#define SOF_DAI_FMT_DSP_B 5 /* L data MSB during FRM LRC */ +#define SOF_DAI_FMT_PDM 6 /* Pulse density modulation */ + +#define SOF_DAI_FMT_CONT (1 << 4) /* continuous clock */ +#define SOF_DAI_FMT_GATED (0 << 4) /* clock is gated */ + +#define SOF_DAI_FMT_NB_NF (0 << 8) /* normal bit clock + frame */ +#define SOF_DAI_FMT_NB_IF (2 << 8) /* normal BCLK + inv FRM */ +#define SOF_DAI_FMT_IB_NF (3 << 8) /* invert BCLK + nor FRM */ +#define SOF_DAI_FMT_IB_IF (4 << 8) /* invert BCLK + FRM */ + +#define SOF_DAI_FMT_CBM_CFM (0 << 12) /* codec clk & FRM master */ +#define SOF_DAI_FMT_CBS_CFM (2 << 12) /* codec clk slave & FRM master */ +#define SOF_DAI_FMT_CBM_CFS (3 << 12) /* codec clk master & frame slave */ +#define SOF_DAI_FMT_CBS_CFS (4 << 12) /* codec clk & FRM slave */ + +#define SOF_DAI_FMT_FORMAT_MASK 0x000f +#define SOF_DAI_FMT_CLOCK_MASK 0x00f0 +#define SOF_DAI_FMT_INV_MASK 0x0f00 +#define SOF_DAI_FMT_MASTER_MASK 0xf000 + +/* types of DAI */ +enum sof_ipc_dai_type { + SOF_DAI_INTEL_NONE = 0, + SOF_DAI_INTEL_SSP, + SOF_DAI_INTEL_DMIC, + SOF_DAI_INTEL_HDA, +}; + +/* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */ +struct sof_ipc_dai_ssp_params { + uint16_t mode; // FIXME: do we need this? + uint16_t clk_id; // FIXME: do we need this? + + uint32_t mclk_rate; /* mclk frequency in Hz */ + uint32_t fsync_rate; /* fsync frequency in Hz */ + uint32_t bclk_rate; /* bclk frequency in Hz */ + + /* TDM */ + uint32_t tdm_slots; + uint32_t rx_slots; + uint32_t tx_slots; + + /* data */ + uint32_t sample_valid_bits; + uint16_t tdm_slot_width; + uint16_t reserved2; /* alignment */ + + /* MCLK */ + uint32_t mclk_direction; + uint32_t mclk_keep_active; + uint32_t bclk_keep_active; + uint32_t fs_keep_active; + + //uint32_t quirks; // FIXME: is 32 bits enough ? + + /* private data, e.g. for quirks */ + //uint32_t pdata[10]; // FIXME: would really need ~16 u32 +} __attribute__((packed)); + +/* HDA Configuration Request - SOF_IPC_DAI_HDA_CONFIG */ +struct sof_ipc_dai_hda_params { + struct sof_ipc_hdr hdr; + /* TODO */ +} __attribute__((packed)); + +/* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */ +struct sof_ipc_dai_dmic_params { + struct sof_ipc_hdr hdr; + /* TODO */ +} __attribute__((packed)); + +/* general purpose DAI configuration */ +struct sof_ipc_dai_config { + struct sof_ipc_hdr hdr; + enum sof_ipc_dai_type type; + uint32_t id; /* physical number if more than 1 of this type */ + + /* physical protocol and clocking */ + uint16_t format; /* SOF_DAI_FMT_ */ + uint16_t reserved; /* alignment */ + + /* HW specific data */ + union { + struct sof_ipc_dai_ssp_params ssp; + struct sof_ipc_dai_hda_params hda; + struct sof_ipc_dai_dmic_params dmic; + }; +}; + +/* + * Stream configuration. + */ + +#define SOF_IPC_MAX_CHANNELS 8 + +/* channel positions - uses same values as ALSA */ +enum sof_ipc_chmap { + SOF_CHMAP_UNKNOWN = 0, + SOF_CHMAP_NA, /* N/A, silent */ + SOF_CHMAP_MONO, /* mono stream */ + SOF_CHMAP_FL, /* front left */ + SOF_CHMAP_FR, /* front right */ + SOF_CHMAP_RL, /* rear left */ + SOF_CHMAP_RR, /* rear right */ + SOF_CHMAP_FC, /* front centre */ + SOF_CHMAP_LFE, /* LFE */ + SOF_CHMAP_SL, /* side left */ + SOF_CHMAP_SR, /* side right */ + SOF_CHMAP_RC, /* rear centre */ + SOF_CHMAP_FLC, /* front left centre */ + SOF_CHMAP_FRC, /* front right centre */ + SOF_CHMAP_RLC, /* rear left centre */ + SOF_CHMAP_RRC, /* rear right centre */ + SOF_CHMAP_FLW, /* front left wide */ + SOF_CHMAP_FRW, /* front right wide */ + SOF_CHMAP_FLH, /* front left high */ + SOF_CHMAP_FCH, /* front centre high */ + SOF_CHMAP_FRH, /* front right high */ + SOF_CHMAP_TC, /* top centre */ + SOF_CHMAP_TFL, /* top front left */ + SOF_CHMAP_TFR, /* top front right */ + SOF_CHMAP_TFC, /* top front centre */ + SOF_CHMAP_TRL, /* top rear left */ + SOF_CHMAP_TRR, /* top rear right */ + SOF_CHMAP_TRC, /* top rear centre */ + SOF_CHMAP_TFLC, /* top front left centre */ + SOF_CHMAP_TFRC, /* top front right centre */ + SOF_CHMAP_TSL, /* top side left */ + SOF_CHMAP_TSR, /* top side right */ + SOF_CHMAP_LLFE, /* left LFE */ + SOF_CHMAP_RLFE, /* right LFE */ + SOF_CHMAP_BC, /* bottom centre */ + SOF_CHMAP_BLC, /* bottom left centre */ + SOF_CHMAP_BRC, /* bottom right centre */ + SOF_CHMAP_LAST = SOF_CHMAP_BRC, +}; + +/* common sample rates for use in masks */ +#define SOF_RATE_8000 (1 << 0) /* 8000Hz */ +#define SOF_RATE_11025 (1 << 1) /* 11025Hz */ +#define SOF_RATE_12000 (1 << 2) /* 12000Hz */ +#define SOF_RATE_16000 (1 << 3) /* 16000Hz */ +#define SOF_RATE_22050 (1 << 4) /* 22050Hz */ +#define SOF_RATE_24000 (1 << 5) /* 24000Hz */ +#define SOF_RATE_32000 (1 << 6) /* 32000Hz */ +#define SOF_RATE_44100 (1 << 7) /* 44100Hz */ +#define SOF_RATE_48000 (1 << 8) /* 48000Hz */ +#define SOF_RATE_64000 (1 << 9) /* 64000Hz */ +#define SOF_RATE_88200 (1 << 10) /* 88200Hz */ +#define SOF_RATE_96000 (1 << 11) /* 96000Hz */ +#define SOF_RATE_176400 (1 << 12) /* 176400Hz */ +#define SOF_RATE_192000 (1 << 13) /* 192000Hz */ + +/* continuous and non-standard rates for flexibility */ +#define SOF_RATE_CONTINUOUS (1 << 30) /* range */ +#define SOF_RATE_KNOT (1 << 31) /* non-continuous */ + +/* stream PCM frame format */ +enum sof_ipc_frame { + SOF_IPC_FRAME_S16_LE = 0, + SOF_IPC_FRAME_S24_4LE, + SOF_IPC_FRAME_S32_LE, + SOF_IPC_FRAME_FLOAT, + /* other formats here */ +}; + +/* stream buffer format */ +enum sof_ipc_buffer_format { + SOF_IPC_BUFFER_INTERLEAVED, + SOF_IPC_BUFFER_NONINTERLEAVED, + /* other formats here */ +}; + +/* stream direction */ +enum sof_ipc_stream_direction { + SOF_IPC_STREAM_PLAYBACK = 0, + SOF_IPC_STREAM_CAPTURE, +}; + +/* stream ring info */ +struct sof_ipc_host_buffer { + uint32_t phy_addr; + uint32_t pages; + uint32_t size; + uint32_t offset; +} __attribute__((packed)); + +struct sof_ipc_stream_params { + struct sof_ipc_host_buffer buffer; + enum sof_ipc_stream_direction direction; + enum sof_ipc_frame frame_fmt; + enum sof_ipc_buffer_format buffer_fmt; + uint32_t stream_tag; + uint32_t rate; + uint32_t channels; + uint32_t sample_valid_bytes; + uint32_t sample_container_bytes; + /* for notifying host period has completed - 0 means no period IRQ */ + uint32_t host_period_bytes; + enum sof_ipc_chmap chmap[SOF_IPC_MAX_CHANNELS]; /* channel map */ +} __attribute__((packed)); + +/* PCM params info - SOF_IPC_STREAM_PCM_PARAMS */ +struct sof_ipc_pcm_params { + struct sof_ipc_hdr hdr; + uint32_t comp_id; + struct sof_ipc_stream_params params; +} __attribute__((packed)); + +/* PCM params info reply - SOF_IPC_STREAM_PCM_PARAMS_REPLY */ +struct sof_ipc_pcm_params_reply { + struct sof_ipc_reply rhdr; + uint32_t comp_id; + uint32_t posn_offset; +} __attribute__((packed)); + +/* compressed vorbis params - SOF_IPC_STREAM_VORBIS_PARAMS */ +struct sof_ipc_vorbis_params { + struct sof_ipc_hdr hdr; + uint32_t comp_id; + struct sof_ipc_stream_params params; + /* TODO */ +} __attribute__((packed)); + +/* free stream - SOF_IPC_STREAM_PCM_PARAMS */ +struct sof_ipc_stream { + struct sof_ipc_hdr hdr; + uint32_t comp_id; +} __attribute__((packed)); + +/* flags indicating which time stamps are in sync with each other */ +#define SOF_TIME_HOST_SYNC (1 << 0) +#define SOF_TIME_DAI_SYNC (1 << 1) +#define SOF_TIME_WALL_SYNC (1 << 2) +#define SOF_TIME_STAMP_SYNC (1 << 3) + +/* flags indicating which time stamps are valid */ +#define SOF_TIME_HOST_VALID (1 << 8) +#define SOF_TIME_DAI_VALID (1 << 9) +#define SOF_TIME_WALL_VALID (1 << 10) +#define SOF_TIME_STAMP_VALID (1 << 11) + +/* flags indicating time stamps are 64bit else 3use low 32bit */ +#define SOF_TIME_HOST_64 (1 << 16) +#define SOF_TIME_DAI_64 (1 << 17) +#define SOF_TIME_WALL_64 (1 << 18) +#define SOF_TIME_STAMP_64 (1 << 19) + +struct sof_ipc_stream_posn { + struct sof_ipc_reply rhdr; + uint32_t comp_id; /* host component ID */ + uint32_t flags; /* SOF_TIME_ */ + uint32_t wallclock_hz; /* frequency of wallclock in Hz */ + uint32_t timestamp_ns; /* resolution of timestamp in ns */ + uint64_t host_posn; /* host DMA position in bytes */ + uint64_t dai_posn; /* DAI DMA position in bytes */ + uint64_t comp_posn; /* comp position in bytes */ + uint64_t wallclock; /* audio wall clock */ + uint64_t timestamp; /* system time stamp */ + uint32_t xrun_comp_id; /* comp ID of XRUN component */ + int32_t xrun_size; /* XRUN size in bytes */ +} __attribute__((packed)); + +/* + * Component Mixers and Controls + */ + +/* control data type and direction */ +enum sof_ipc_ctrl_type { + /* per channel data - uses struct sof_ipc_ctrl_value_chan */ + SOF_CTRL_TYPE_VALUE_CHAN_GET = 0, + SOF_CTRL_TYPE_VALUE_CHAN_SET, + /* component data - uses struct sof_ipc_ctrl_value_comp */ + SOF_CTRL_TYPE_VALUE_COMP_GET, + SOF_CTRL_TYPE_VALUE_COMP_SET, + /* bespoke data - struct struct sof_abi_hdr */ + SOF_CTRL_TYPE_DATA_GET, + SOF_CTRL_TYPE_DATA_SET, +}; + +/* control command type */ +enum sof_ipc_ctrl_cmd { + SOF_CTRL_CMD_VOLUME = 0, /* maps to ALSA volume style controls */ + SOF_CTRL_CMD_ENUM, /* maps to ALSA enum style controls */ + SOF_CTRL_CMD_SWITCH, /* maps to ALSA switch style controls */ + SOF_CTRL_CMD_BINARY, /* maps to ALSA binary style controls */ +}; + +/* generic channel mapped value data */ +struct sof_ipc_ctrl_value_chan { + enum sof_ipc_chmap channel; + uint32_t value; +} __attribute__((packed)); + +/* generic component mapped value data */ +struct sof_ipc_ctrl_value_comp { + uint32_t index; /* component source/sink/control index in control */ + union { + uint32_t uvalue; + int32_t svalue; + }; +} __attribute__((packed)); + +/* generic control data */ +struct sof_ipc_ctrl_data { + struct sof_ipc_reply rhdr; + uint32_t comp_id; + + /* control access and data type */ + enum sof_ipc_ctrl_type type; + enum sof_ipc_ctrl_cmd cmd; + uint32_t index; /* control index for comps > 1 control */ + + /* control data - can either be appended or DMAed from host */ + struct sof_ipc_host_buffer buffer; + uint32_t num_elems; /* in array elems or bytes */ + + /* control data - add new types if needed */ + union { + /* channel values can be used by volume type controls */ + struct sof_ipc_ctrl_value_chan chanv[0]; + /* component values used by routing controls like mux, mixer */ + struct sof_ipc_ctrl_value_comp compv[0]; + /* data can be used by binary controls */ + struct sof_abi_hdr data[0]; + }; +} __attribute__((packed)); + +/* + * Component + */ + +/* types of component */ +enum sof_comp_type { + SOF_COMP_NONE = 0, + SOF_COMP_HOST, + SOF_COMP_DAI, + SOF_COMP_SG_HOST, /* scatter gather variant */ + SOF_COMP_SG_DAI, /* scatter gather variant */ + SOF_COMP_VOLUME, + SOF_COMP_MIXER, + SOF_COMP_MUX, + SOF_COMP_SRC, + SOF_COMP_SPLITTER, + SOF_COMP_TONE, + SOF_COMP_SWITCH, + SOF_COMP_BUFFER, + SOF_COMP_EQ_IIR, + SOF_COMP_EQ_FIR, + SOF_COMP_FILEREAD, /* host test based file IO */ + SOF_COMP_FILEWRITE, /* host test based file IO */ +}; + +/* XRUN action for component */ +#define SOF_XRUN_STOP 1 /* stop stream */ +#define SOF_XRUN_UNDER_ZERO 2 /* send 0s to sink */ +#define SOF_XRUN_OVER_NULL 4 /* send data to NULL */ + +/* create new generic component - SOF_IPC_TPLG_COMP_NEW */ +struct sof_ipc_comp { + struct sof_ipc_hdr hdr; + uint32_t id; + enum sof_comp_type type; + uint32_t pipeline_id; +} __attribute__((packed)); + +/* + * Component Buffers + */ + +/* create new component buffer - SOF_IPC_TPLG_BUFFER_NEW */ +struct sof_ipc_buffer { + struct sof_ipc_comp comp; + uint32_t size; /* buffer size in bytes */ + uint32_t caps; /* SOF_MEM_CAPS_ */ +} __attribute__((packed)); + +/* generic component config data - must always be after struct sof_ipc_comp */ +struct sof_ipc_comp_config { + uint32_t periods_sink; /* 0 means variable */ + uint32_t periods_source; /* 0 means variable */ + uint32_t preload_count; /* how many periods to preload */ + enum sof_ipc_frame frame_fmt; + uint32_t xrun_action; +} __attribute__((packed)); + +/* generic host component */ +struct sof_ipc_comp_host { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + enum sof_ipc_stream_direction direction; + uint32_t no_irq; /* don't send periodic IRQ to host/DSP */ + uint32_t dmac_id; + uint32_t dmac_chan; + uint32_t dmac_config; /* DMA engine specific */ +} __attribute__((packed)); + +/* generic DAI component */ +struct sof_ipc_comp_dai { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + enum sof_ipc_stream_direction direction; + uint32_t index; + enum sof_ipc_dai_type type; + uint32_t dmac_id; + uint32_t dmac_chan; + uint32_t dmac_config; /* DMA engine specific */ +} __attribute__((packed)); + +/* generic mixer component */ +struct sof_ipc_comp_mixer { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* volume ramping types */ +enum sof_volume_ramp { + SOF_VOLUME_LINEAR = 0, + SOF_VOLUME_LOG, + SOF_VOLUME_LINEAR_ZC, + SOF_VOLUME_LOG_ZC, +}; + +/* generic volume component */ +struct sof_ipc_comp_volume { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + uint32_t channels; + int32_t min_value; + int32_t max_value; + enum sof_volume_ramp ramp; + uint32_t initial_ramp; /* ramp space in ms */ +} __attribute__((packed)); + +/* generic SRC component */ +struct sof_ipc_comp_src { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + /* either source or sink rate must be non zero */ + uint32_t source_rate; /* source rate or 0 for variable */ + uint32_t sink_rate; /* sink rate or 0 for variable */ + uint32_t rate_mask; /* SOF_RATE_ supported rates */ +} __attribute__((packed)); + +/* generic MUX component */ +struct sof_ipc_comp_mux { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* generic tone generator component */ +struct sof_ipc_comp_tone { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + int32_t frequency; + int32_t amplitude; + int32_t freq_mult; + int32_t ampl_mult; + int32_t length; + int32_t period; + int32_t repeats; + int32_t ramp_step; +} __attribute__((packed)); + +/* FIR equalizer component */ +struct sof_ipc_comp_eq_fir { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* IIR equalizer component */ +struct sof_ipc_comp_eq_iir { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; +} __attribute__((packed)); + +/* frees components, buffers and pipelines + * SOF_IPC_TPLG_COMP_FREE, SOF_IPC_TPLG_PIPE_FREE, SOF_IPC_TPLG_BUFFER_FREE + */ +struct sof_ipc_free { + struct sof_ipc_hdr hdr; + uint32_t id; +} __attribute__((packed)); + +struct sof_ipc_comp_reply { + struct sof_ipc_reply rhdr; + uint32_t id; + uint32_t offset; +} __attribute__((packed)); + +/* + * Pipeline + */ + +/* new pipeline - SOF_IPC_TPLG_PIPE_NEW */ +struct sof_ipc_pipe_new { + struct sof_ipc_hdr hdr; + uint32_t comp_id; /* component id for pipeline */ + uint32_t pipeline_id; /* pipeline id */ + uint32_t sched_id; /* sheduling component id */ + uint32_t core; /* core we run on */ + uint32_t deadline; /* execution completion deadline in us*/ + uint32_t priority; /* priority level 0 (low) to 10 (max) */ + uint32_t mips; /* worst case instruction count per period */ + uint32_t frames_per_sched;/* output frames of pipeline, 0 is variable */ + uint32_t xrun_limit_usecs; /* report xruns greater than limit */ + uint32_t timer;/* non zero if timer scheduled otherwise DAI scheduled */ +} __attribute__((packed)); + +/* pipeline construction complete - SOF_IPC_TPLG_PIPE_COMPLETE */ +struct sof_ipc_pipe_ready { + struct sof_ipc_hdr hdr; + uint32_t comp_id; +} __attribute__((packed)); + +struct sof_ipc_pipe_free { + struct sof_ipc_hdr hdr; + uint32_t comp_id; +} __attribute__((packed)); + +/* connect two components in pipeline - SOF_IPC_TPLG_COMP_CONNECT */ +struct sof_ipc_pipe_comp_connect { + struct sof_ipc_hdr hdr; + uint32_t source_id; + uint32_t sink_id; +} __attribute__((packed)); + +/* + * PM + */ + +/* PM context element */ +struct sof_ipc_pm_ctx_elem { + uint32_t type; + uint32_t size; + uint64_t addr; +} __attribute__((packed)); + +/* + * PM context - SOF_IPC_PM_CTX_SAVE, SOF_IPC_PM_CTX_RESTORE, + * SOF_IPC_PM_CTX_SIZE + */ +struct sof_ipc_pm_ctx { + struct sof_ipc_hdr hdr; + struct sof_ipc_host_buffer buffer; + uint32_t num_elems; + uint32_t size; + struct sof_ipc_pm_ctx_elem elems[]; +}; + +/* + * Firmware boot and version + */ + +#define SOF_IPC_MAX_ELEMS 16 + +/* extended data types that can be appended onto end of sof_ipc_fw_ready */ +enum sof_ipc_ext_data { + SOF_IPC_EXT_DMA_BUFFER = 0, + SOF_IPC_EXT_WINDOW, +}; + +/* FW version - SOF_IPC_GLB_VERSION */ +struct sof_ipc_fw_version { + uint16_t major; + uint16_t minor; + uint16_t build; + uint8_t date[12]; + uint8_t time[10]; + uint8_t tag[6]; +} __attribute__((packed)); + +/* FW ready Message - sent by firmware when boot has completed */ +struct sof_ipc_fw_ready { + struct sof_ipc_hdr hdr; + uint32_t dspbox_offset; /* dsp initiated IPC mailbox */ + uint32_t hostbox_offset; /* host initiated IPC mailbox */ + uint32_t dspbox_size; + uint32_t hostbox_size; + struct sof_ipc_fw_version version; +} __attribute__((packed)); + +/* + * Extended Firmware data. All optional, depends on platform/arch. + */ + +enum sof_ipc_region { + SOF_IPC_REGION_DOWNBOX = 0, + SOF_IPC_REGION_UPBOX, + SOF_IPC_REGION_TRACE, + SOF_IPC_REGION_DEBUG, + SOF_IPC_REGION_STREAM, + SOF_IPC_REGION_REGS, + SOF_IPC_REGION_EXCEPTION, +}; + +struct sof_ipc_ext_data_hdr { + struct sof_ipc_hdr hdr; + enum sof_ipc_ext_data type; /* SOF_IPC_EXT_ */ +}; + +struct sof_ipc_dma_buffer_elem { + enum sof_ipc_region type; + uint32_t id; /* platform specific - used to map to host memory */ + struct sof_ipc_host_buffer buffer; +}; + +/* extended data DMA buffers for IPC, trace and debug */ +struct sof_ipc_dma_buffer_data { + struct sof_ipc_ext_data_hdr ext_hdr; + uint32_t num_buffers; + /* host files in buffer[n].buffer */ + struct sof_ipc_dma_buffer_elem buffer[]; +} __attribute__((packed)); + +struct sof_ipc_window_elem { + enum sof_ipc_region type; + uint32_t id; /* platform specific - used to map to host memory */ + uint32_t flags; /* R, W, RW, etc - to define */ + uint32_t size; /* size of region in bytes */ + /* offset in window region as windows can be partitioned */ + uint32_t offset; +}; + +/* extended data memory windows for IPC, trace and debug */ +struct sof_ipc_window { + struct sof_ipc_ext_data_hdr ext_hdr; + uint32_t num_windows; + struct sof_ipc_window_elem window[]; +} __attribute__((packed)); + +/* + * DMA for Trace + */ + +/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */ +struct sof_ipc_dma_trace_params { + struct sof_ipc_hdr hdr; + struct sof_ipc_host_buffer buffer; + uint32_t stream_tag; +} __attribute__((packed)); + +/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */ +struct sof_ipc_dma_trace_posn { + struct sof_ipc_reply rhdr; + uint32_t host_offset; /* Offset of DMA host buffer */ + uint32_t overflow; /* overflow bytes if any */ + uint32_t messages; /* total trace messages */ +} __attribute__((packed)); + +/* + * Architecture specific debug + */ + +/* Xtensa Firmware Oops data */ +struct sof_ipc_dsp_oops_xtensa { + uint32_t exccause; + uint32_t excvaddr; + uint32_t ps; + uint32_t epc1; + uint32_t epc2; + uint32_t epc3; + uint32_t epc4; + uint32_t epc5; + uint32_t epc6; + uint32_t epc7; + uint32_t eps2; + uint32_t eps3; + uint32_t eps4; + uint32_t eps5; + uint32_t eps6; + uint32_t eps7; + uint32_t depc; + uint32_t intenable; + uint32_t interrupt; + uint32_t sar; + uint32_t stack; +} __attribute__((packed)); + +#endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c new file mode 100644 index 00000000000000..bd646c23cf9fd2 --- /dev/null +++ b/sound/soc/sof/ipc.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +/* IPC message timeout (msecs) */ +#define IPC_TIMEOUT_MSECS 300 + +#define IPC_EMPTY_LIST_SIZE 8 + +/* SOF generic IPC data */ +struct snd_sof_ipc { + struct snd_sof_dev *sdev; + + /* TX message work and status */ + wait_queue_head_t wait_txq; + struct work_struct tx_kwork; + bool msg_pending; + + /* Rx Message work and status */ + struct work_struct rx_kwork; + + /* lists */ + struct list_head tx_list; + struct list_head reply_list; + struct list_head empty_list; +}; + +/* locks held by caller */ +static struct snd_sof_ipc_msg *msg_get_empty(struct snd_sof_ipc *ipc) +{ + struct snd_sof_ipc_msg *msg = NULL; + + if (!list_empty(&ipc->empty_list)) { + msg = list_first_entry(&ipc->empty_list, struct snd_sof_ipc_msg, + list); + list_del(&msg->list); + } + + return msg; +} + +static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, + void *reply_data) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_hdr *hdr = (struct sof_ipc_hdr *)msg->msg_data; + unsigned long flags; + int ret; + + /* wait for DSP IPC completion */ + ret = wait_event_timeout(msg->waitq, msg->complete, + msecs_to_jiffies(IPC_TIMEOUT_MSECS)); + + spin_lock_irqsave(&sdev->ipc_lock, flags); + + if (ret == 0) { + dev_err(sdev->dev, "error: ipc timed out for 0x%x size 0x%x\n", + hdr->cmd, hdr->size); + snd_sof_dsp_dbg_dump(ipc->sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + snd_sof_trace_notify_for_error(ipc->sdev); + ret = -ETIMEDOUT; + } else { + /* copy the data returned from DSP */ + ret = snd_sof_dsp_get_reply(sdev, msg); + if (msg->reply_size) + memcpy(reply_data, msg->reply_data, msg->reply_size); + if (ret < 0) + dev_err(sdev->dev, "error: ipc error for 0x%x size 0x%zx\n", + hdr->cmd, msg->reply_size); + else + dev_dbg(sdev->dev, "ipc: 0x%x succeeded\n", hdr->cmd); + } + + /* return message body to empty list */ + list_move(&msg->list, &ipc->empty_list); + + spin_unlock_irqrestore(&sdev->ipc_lock, flags); + + /* continue to schedule any remaining messages... */ + snd_sof_ipc_msgs_tx(sdev); + + return ret; +} + +int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, + void *msg_data, size_t msg_bytes, void *reply_data, + size_t reply_bytes) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg; + unsigned long flags; + + spin_lock_irqsave(&sdev->ipc_lock, flags); + + msg = msg_get_empty(ipc); + if (!msg) { + spin_unlock_irqrestore(&sdev->ipc_lock, flags); + return -EBUSY; + } + + msg->header = header; + msg->msg_size = msg_bytes; + msg->reply_size = reply_bytes; + msg->complete = false; + + if (msg_bytes) + memcpy(msg->msg_data, msg_data, msg_bytes); + + list_add_tail(&msg->list, &ipc->tx_list); + + /* schedule the messgae if not busy */ + if (snd_sof_dsp_is_ready(sdev)) + schedule_work(&ipc->tx_kwork); + + spin_unlock_irqrestore(&sdev->ipc_lock, flags); + + return tx_wait_done(ipc, msg, reply_data); +} +EXPORT_SYMBOL(sof_ipc_tx_message); + +static void ipc_tx_next_msg(struct work_struct *work) +{ + struct snd_sof_ipc *ipc = + container_of(work, struct snd_sof_ipc, tx_kwork); + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg; + + spin_lock_irq(&sdev->ipc_lock); + + if (list_empty(&ipc->tx_list)) + goto out; + + msg = list_first_entry(&ipc->tx_list, struct snd_sof_ipc_msg, list); + list_move(&msg->list, &ipc->reply_list); + + snd_sof_dsp_send_msg(sdev, msg); + dev_dbg(sdev->dev, "ipc: send 0x%x\n", msg->header); + +out: + spin_unlock_irq(&sdev->ipc_lock); +} + +struct snd_sof_ipc_msg *sof_ipc_reply_find_msg(struct snd_sof_ipc *ipc, + u32 header) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg; + + header = SOF_IPC_MESSAGE_ID(header); + + if (list_empty(&ipc->reply_list)) + goto err; + + list_for_each_entry(msg, &ipc->reply_list, list) { + if (SOF_IPC_MESSAGE_ID(msg->header) == header) + return msg; + } + +err: + dev_err(sdev->dev, "error: rx list empty but received 0x%x\n", + header); + return NULL; +} +EXPORT_SYMBOL(sof_ipc_reply_find_msg); + +/* locks held by caller */ +void sof_ipc_tx_msg_reply_complete(struct snd_sof_ipc *ipc, + struct snd_sof_ipc_msg *msg) +{ + msg->complete = true; + wake_up(&msg->waitq); +} + +void sof_ipc_drop_all(struct snd_sof_ipc *ipc) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc_msg *msg, *tmp; + unsigned long flags; + + /* drop all TX and Rx messages before we stall + reset DSP */ + spin_lock_irqsave(&sdev->ipc_lock, flags); + + list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) { + list_move(&msg->list, &ipc->empty_list); + dev_err(sdev->dev, "error: dropped msg %d\n", msg->header); + } + + list_for_each_entry_safe(msg, tmp, &ipc->reply_list, list) { + list_move(&msg->list, &ipc->empty_list); + dev_err(sdev->dev, "error: dropped reply %d\n", msg->header); + } + + spin_unlock_irqrestore(&sdev->ipc_lock, flags); +} +EXPORT_SYMBOL(sof_ipc_drop_all); + +void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct snd_sof_ipc_msg *msg; + + msg = sof_ipc_reply_find_msg(sdev->ipc, msg_id); + if (!msg) { + dev_err(sdev->dev, "error: can't find message header 0x%x", + msg_id); + return; + } + + /* wake up and return the error if we have waiters on this message ? */ + sof_ipc_tx_msg_reply_complete(sdev->ipc, msg); +} +EXPORT_SYMBOL(snd_sof_ipc_reply); + +int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, + size_t dspbox_size, u32 hostbox, + size_t hostbox_size) +{ + sdev->dsp_box.offset = dspbox; + sdev->dsp_box.size = dspbox_size; + sdev->host_box.offset = hostbox; + sdev->host_box.size = hostbox_size; + return 0; +} +EXPORT_SYMBOL(snd_sof_dsp_mailbox_init); + +static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + u32 posn_offset; + int direction; + + /* check if we have stream box */ + if (sdev->stream_box.size == 0) { + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + + spcm = snd_sof_find_spcm_comp(sdev, posn.comp_id, &direction); + } else { + spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction); + } + + if (!spcm) { + dev_err(sdev->dev, + "period elapsed for unknown stream, msg_id %d\n", + msg_id); + return; + } + + /* have stream box read from stream box */ + if (sdev->stream_box.size != 0) { + posn_offset = spcm->posn_offset[direction]; + snd_sof_dsp_mailbox_read(sdev, posn_offset, &posn, + sizeof(posn)); + + dev_dbg(sdev->dev, "posn mailbox: posn offset is 0x%x", + posn_offset); + } + + dev_dbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n", + posn.host_posn, posn.dai_posn, posn.wallclock); + + memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); + snd_pcm_period_elapsed(spcm->stream[direction].substream); +} + +static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + u32 posn_offset; + int direction; + + /* check if we have stream box */ + if (sdev->stream_box.size == 0) { + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + + spcm = snd_sof_find_spcm_comp(sdev, posn.comp_id, &direction); + } else { + spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction); + } + + if (!spcm) { + dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n", + msg_id); + return; + } + + /* have stream box read from stream box */ + if (sdev->stream_box.size != 0) { + posn_offset = spcm->posn_offset[direction]; + snd_sof_dsp_mailbox_read(sdev, posn_offset, &posn, + sizeof(posn)); + + dev_dbg(sdev->dev, "posn mailbox: posn offset is 0x%x", + posn_offset); + } + + dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", + posn.host_posn, posn.xrun_comp_id, posn.xrun_size); + + return; /* TODO: don't do anything yet until preload is working */ + + memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); + snd_pcm_stop_xrun(spcm->stream[direction].substream); +} + +static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) +{ + /* get msg cmd type and msd id */ + u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK; + u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd); + + switch (msg_type) { + case SOF_IPC_STREAM_POSITION: + ipc_period_elapsed(sdev, msg_id); + break; + case SOF_IPC_STREAM_TRIG_XRUN: + ipc_xrun(sdev, msg_id); + break; + default: + dev_err(sdev->dev, "error: unhandled stream message %x\n", + msg_id); + break; + } +} + +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_dma_trace_posn posn; + + switch (msg_id) { + case SOF_IPC_TRACE_DMA_POSITION: + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + snd_sof_trace_update_pos(sdev, &posn); + break; + default: + dev_err(sdev->dev, "error: unhandled trace message %x\n", + msg_id); + break; + } +} + +/* DSP firmware has sent host a message */ +static void ipc_msgs_rx(struct work_struct *work) +{ + struct snd_sof_ipc *ipc = + container_of(work, struct snd_sof_ipc, rx_kwork); + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_hdr hdr; + u32 cmd, type; + int err = -EINVAL; + + /* read back header */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &hdr, sizeof(hdr)); + + cmd = hdr.cmd & SOF_GLB_TYPE_MASK; + type = hdr.cmd & SOF_CMD_TYPE_MASK; + + switch (cmd) { + case SOF_IPC_GLB_REPLY: + dev_err(sdev->dev, "error: ipc reply unknown\n"); + break; + case SOF_IPC_FW_READY: + /* check for FW boot completion */ + if (!sdev->boot_complete) { + if (sdev->ops->fw_ready) + err = sdev->ops->fw_ready(sdev, cmd); + if (err < 0) { + dev_err(sdev->dev, "DSP firmware boot timeout %d\n", + err); + } else { + /* firmware boot completed OK */ + sdev->boot_complete = true; + dev_dbg(sdev->dev, "booting DSP firmware completed\n"); + wake_up(&sdev->boot_wait); + } + } + break; + case SOF_IPC_GLB_COMPOUND: + case SOF_IPC_GLB_TPLG_MSG: + case SOF_IPC_GLB_PM_MSG: + case SOF_IPC_GLB_COMP_MSG: + break; + case SOF_IPC_GLB_STREAM_MSG: + /* need to pass msg id into the function */ + ipc_stream_message(sdev, hdr.cmd); + break; + case SOF_IPC_GLB_TRACE_MSG: + ipc_trace_message(sdev, type); + break; + default: + dev_err(sdev->dev, "unknown DSP message 0x%x\n", cmd); + break; + } + + dev_dbg(sdev->dev, "ipc rx: 0x%x done\n", hdr.cmd); + + snd_sof_dsp_cmd_done(sdev); +} + +void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->tx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_tx); + +void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); + +struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc *ipc; + struct snd_sof_ipc_msg *msg; + int i; + + ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL); + if (!ipc) + return NULL; + + INIT_LIST_HEAD(&ipc->tx_list); + INIT_LIST_HEAD(&ipc->reply_list); + INIT_LIST_HEAD(&ipc->empty_list); + init_waitqueue_head(&ipc->wait_txq); + INIT_WORK(&ipc->tx_kwork, ipc_tx_next_msg); + INIT_WORK(&ipc->rx_kwork, ipc_msgs_rx); + ipc->sdev = sdev; + + /* pre-allocate messages */ + dev_dbg(sdev->dev, "pre-allocate %d IPC messages\n", + IPC_EMPTY_LIST_SIZE); + msg = devm_kzalloc(sdev->dev, sizeof(struct snd_sof_ipc_msg) * + IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (!msg) + return NULL; + + /* pre-allocate message data */ + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + msg->msg_data = devm_kzalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); + if (!msg->msg_data) + return NULL; + + msg->reply_data = devm_kzalloc(sdev->dev, PAGE_SIZE, + GFP_KERNEL); + if (!msg->reply_data) + return NULL; + + init_waitqueue_head(&msg->waitq); + list_add(&msg->list, &ipc->empty_list); + msg++; + } + + return ipc; +} +EXPORT_SYMBOL(snd_sof_ipc_init); + +void snd_sof_ipc_free(struct snd_sof_dev *sdev) +{ + /* TODO: send IPC to prepare DSP for shutdown */ + cancel_work_sync(&sdev->ipc->tx_kwork); + cancel_work_sync(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_free); + +int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm, int direction, + struct sof_ipc_stream_posn *posn) +{ + struct sof_ipc_stream stream; + int err; + + /* read position via slower IPC */ + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION; + stream.comp_id = spcm->stream[direction].comp_id; + + /* send IPC to the DSP */ + err = sof_ipc_tx_message(sdev->ipc, + stream.hdr.cmd, &stream, sizeof(stream), &posn, + sizeof(*posn)); + if (err < 0) { + dev_err(sdev->dev, "error: failed to get stream %d position\n", + stream.comp_id); + return err; + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_stream_posn); + +int snd_sof_ipc_set_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + int err; + + /* read firmware volume */ + if (scontrol->readback_offset != 0) { + /* we can read value header via mmaped region */ + snd_sof_dsp_block_write(sdev, scontrol->readback_offset, + cdata->chanv, + sizeof(struct sof_ipc_ctrl_value_chan) * + cdata->num_elems); + + } else { + /* write value via slower IPC */ + cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; + cdata->cmd = ctrl_cmd; + cdata->type = ctrl_type; + cdata->rhdr.hdr.size = scontrol->size; + cdata->comp_id = scontrol->comp_id; + cdata->num_elems = scontrol->num_channels; + + /* send IPC to the DSP */ + err = sof_ipc_tx_message(sdev->ipc, + cdata->rhdr.hdr.cmd, cdata, + cdata->rhdr.hdr.size, + cdata, cdata->rhdr.hdr.size); + if (err < 0) { + dev_err(sdev->dev, "error: failed to set control %d values\n", + cdata->comp_id); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_set_comp_data); + +int snd_sof_ipc_get_comp_data(struct snd_sof_ipc *ipc, + struct snd_sof_control *scontrol, u32 ipc_cmd, + enum sof_ipc_ctrl_type ctrl_type, + enum sof_ipc_ctrl_cmd ctrl_cmd) +{ + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + int err; + + /* read firmware byte counters */ + if (scontrol->readback_offset != 0) { + /* we can read values via mmaped region */ + snd_sof_dsp_block_read(sdev, scontrol->readback_offset, + cdata->chanv, + sizeof(struct sof_ipc_ctrl_value_chan) * + cdata->num_elems); + + } else { + /* read position via slower IPC */ + cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; + cdata->cmd = ctrl_cmd; + cdata->type = ctrl_type; + cdata->rhdr.hdr.size = scontrol->size; + cdata->comp_id = scontrol->comp_id; + cdata->num_elems = scontrol->num_channels; + + /* send IPC to the DSP */ + err = sof_ipc_tx_message(sdev->ipc, + cdata->rhdr.hdr.cmd, cdata, + cdata->rhdr.hdr.size, + cdata, cdata->rhdr.hdr.size); + if (err < 0) { + dev_err(sdev->dev, "error: failed to get control %d values\n", + cdata->comp_id); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_ipc_get_comp_data); From 9c9d49ddf89370f3886ea1e9ca9d1d96d7003762 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:32:38 +0000 Subject: [PATCH 110/285] ASoC: SOF: Add PCM operations support Add support for exposing PCMs to userspace. PCMs are defined by topology and the operations in this patch map to SOF IPC calls. Signed-off-by: Liam Girdwood --- sound/soc/sof/pcm.c | 645 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 645 insertions(+) create mode 100644 sound/soc/sof/pcm.c diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c new file mode 100644 index 00000000000000..32d114ee748f7e --- /dev/null +++ b/sound/soc/sof/pcm.c @@ -0,0 +1,645 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* Create DMA buffer page table for DSP */ +static int create_page_table(struct snd_pcm_substream *substream, + unsigned char *dma_area, size_t size) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); + int stream = substream->stream; + + return snd_sof_create_page_table(sdev, dmab, + spcm->stream[stream].page_table.area, size); +} + +/* this may get called several times by oss emulation */ +static int sof_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + const struct snd_sof_dsp_ops *ops = sdev->ops; + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_pcm_params pcm; + struct sof_ipc_pcm_params_reply ipc_params_reply; + int posn_offset; + int ret; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: hw params stream %d dir %d\n", + spcm->pcm.pcm_id, substream->stream); + + memset(&pcm, 0, sizeof(pcm)); + + /* allocate audio buffer pages */ + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) { + dev_err(sdev->dev, "error: could not allocate %d bytes for PCM %d\n", + params_buffer_bytes(params), ret); + return ret; + } + + /* craete compressed page table for audio firmware */ + ret = create_page_table(substream, runtime->dma_area, + runtime->dma_bytes); + if (ret < 0) + return ret; + + /* number of pages should be rounded up */ + if (runtime->dma_bytes % PAGE_SIZE) + pcm.params.buffer.pages = (runtime->dma_bytes / PAGE_SIZE) + 1; + else + pcm.params.buffer.pages = runtime->dma_bytes / PAGE_SIZE; + + /* set IPC PCM parameters */ + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + pcm.comp_id = spcm->stream[substream->stream].comp_id; + pcm.params.buffer.phy_addr = + spcm->stream[substream->stream].page_table.addr; + pcm.params.buffer.size = runtime->dma_bytes; + pcm.params.buffer.offset = 0; + pcm.params.direction = substream->stream; + pcm.params.sample_valid_bytes = params_width(params) >> 3; + pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + pcm.params.rate = params_rate(params); + pcm.params.channels = params_channels(params); + pcm.params.host_period_bytes = params_period_bytes(params); + + /* container size */ + switch (params_width(params)) { + case 16: + pcm.params.sample_container_bytes = 2; + break; + case 24: + pcm.params.sample_container_bytes = 4; + break; + case 32: + pcm.params.sample_container_bytes = 4; + break; + default: + return -EINVAL; + } + + /* format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case SNDRV_PCM_FORMAT_S24: + pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; + break; + case SNDRV_PCM_FORMAT_S32: + pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + case SNDRV_PCM_FORMAT_FLOAT: + pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT; + break; + default: + return -EINVAL; + } + + /* firmware already configured host stream */ + if (ops && ops->host_stream_hw_params) { + pcm.params.stream_tag = + ops->host_stream_hw_params(sdev, substream, params); + dev_dbg(sdev->dev, "stream_tag %d", pcm.params.stream_tag); + } + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + &ipc_params_reply, sizeof(ipc_params_reply)); + + /* validate offset */ + posn_offset = ipc_params_reply.posn_offset; + + /* check if offset is overflow or it is not aligned */ + if (posn_offset > sdev->stream_box.size || + posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) { + dev_err(sdev->dev, "error: got wrong posn offset 0x%x for PCM %d\n", + posn_offset, ret); + return ret; + } + spcm->posn_offset[substream->stream] = + sdev->stream_box.offset + posn_offset; + + return ret; +} + +static int sof_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + int ret; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id, + substream->stream); + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + + snd_pcm_lib_free_pages(substream); + return ret; +} + +static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + const struct snd_sof_dsp_ops *ops = sdev->ops; + int ret = 0; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: trigger stream %d dir %d cmd %d\n", + spcm->pcm.pcm_id, substream->stream, cmd); + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + break; + case SNDRV_PCM_TRIGGER_STOP: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_SUSPEND: + break; + default: + dev_err(sdev->dev, "error: unhandled trigger cmd %d\n", cmd); + return -EINVAL; + } + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + + if (ops && ops->host_stream_trigger) + ret = ops->host_stream_trigger(sdev, substream, cmd); + + return ret; +} + +static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + snd_pcm_uframes_t host = 0, dai = 0; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + /* TODO: call HW position callback */ + host = bytes_to_frames(substream->runtime, + spcm->stream[substream->stream].posn.host_posn); + dai = bytes_to_frames(substream->runtime, + spcm->stream[substream->stream].posn.dai_posn); + + dev_dbg(sdev->dev, "PCM: stream %d dir %d DMA position %lu DAI position %lu\n", + spcm->pcm.pcm_id, substream->stream, host, dai); + + return host; +} + +static int sof_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct snd_soc_tplg_stream_caps *caps = + &spcm->pcm.caps[substream->stream]; + const struct snd_sof_dsp_ops *ops = sdev->ops; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: open stream %d dir %d\n", spcm->pcm.pcm_id, + substream->stream); + + mutex_lock(&spcm->mutex); + + pm_runtime_get_sync(sdev->dev); + + /* set any runtime constraints based on topology */ + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + caps->period_size_min); + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + caps->period_size_min); + + /* set runtime config */ + runtime->hw.info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; + runtime->hw.formats = caps->formats; + runtime->hw.period_bytes_min = caps->period_size_min; + runtime->hw.period_bytes_max = caps->period_size_max; + runtime->hw.periods_min = caps->periods_min; + runtime->hw.periods_max = caps->periods_max; + runtime->hw.buffer_bytes_max = caps->buffer_size_max; + + dev_dbg(sdev->dev, "period min %zd max %zd bytes\n", + runtime->hw.period_bytes_min, + runtime->hw.period_bytes_max); + dev_dbg(sdev->dev, "period count %d max %d\n", + runtime->hw.periods_min, + runtime->hw.periods_max); + dev_dbg(sdev->dev, "buffer max %zd bytes\n", + runtime->hw.buffer_bytes_max); + + // TODO: create IPC to get this from DSP pipeline + //runtime->hw.fifo_size = hw->fifo_size; + + /* set wait time - TODO: come from topology */ + snd_pcm_wait_time(substream, 500); + + spcm->stream[substream->stream].posn.host_posn = 0; + spcm->stream[substream->stream].posn.dai_posn = 0; + spcm->stream[substream->stream].substream = substream; + + if (ops && ops->host_stream_open) + ops->host_stream_open(sdev, substream); + + mutex_unlock(&spcm->mutex); + return 0; +} + +static int sof_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + const struct snd_sof_dsp_ops *ops = sdev->ops; + + /* nothing todo for BE */ + if (rtd->dai_link->no_pcm) + return 0; + + dev_dbg(sdev->dev, "pcm: close stream %d dir %d\n", spcm->pcm.pcm_id, + substream->stream); + + if (ops && ops->host_stream_close) + ops->host_stream_close(sdev, substream); + + mutex_lock(&spcm->mutex); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + mutex_unlock(&spcm->mutex); + return 0; +} + +static struct snd_pcm_ops sof_pcm_ops = { + .open = sof_pcm_open, + .close = sof_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sof_pcm_hw_params, + .hw_free = sof_pcm_hw_free, + .trigger = sof_pcm_trigger, + .pointer = sof_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm; + struct snd_pcm *pcm = rtd->pcm; + struct snd_soc_tplg_stream_caps *caps; + int ret = 0, stream = SNDRV_PCM_STREAM_PLAYBACK; + + spcm = snd_sof_find_spcm_dai(sdev, rtd); + + if (!spcm) { + dev_warn(sdev->dev, "warn: can't find PCM with DAI ID %d\n", + rtd->dai_link->id); + return 0; + } + rtd->sof = spcm; + + dev_dbg(sdev->dev, "creating new PCM %s\n", spcm->pcm.pcm_name); + + /* do we need to allocate playback PCM DMA pages */ + if (!spcm->pcm.playback) + goto capture; + + caps = &spcm->pcm.caps[stream]; + + /* pre-allocate playback audio buffer pages */ + dev_dbg(sdev->dev, "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n", + caps->name, caps->buffer_size_min, caps->buffer_size_max); + + ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, + SNDRV_DMA_TYPE_DEV_SG, sdev->parent, + caps->buffer_size_min, + caps->buffer_size_max); + if (ret) { + dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", + caps->buffer_size_min, caps->buffer_size_max, + caps->name, ret); + return ret; + } + + /* allocate playback page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, + PAGE_SIZE, &spcm->stream[stream].page_table); + if (ret < 0) { + dev_err(sdev->dev, "error: can't alloc page table for %s %d\n", + caps->name, ret); + return ret; + } + +capture: + stream = SNDRV_PCM_STREAM_CAPTURE; + + /* do we need to allocate capture PCM DMA pages */ + if (!spcm->pcm.capture) + return ret; + + caps = &spcm->pcm.caps[stream]; + + /* pre-allocate capture audio buffer pages */ + dev_dbg(sdev->dev, "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n", + caps->name, caps->buffer_size_min, caps->buffer_size_max); + + ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, + SNDRV_DMA_TYPE_DEV_SG, sdev->parent, + caps->buffer_size_min, + caps->buffer_size_max); + if (ret) { + dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", + caps->buffer_size_min, caps->buffer_size_max, + caps->name, ret); + snd_dma_free_pages(&spcm->stream[stream].page_table); + return ret; + } + + /* allocate capture page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, + PAGE_SIZE, &spcm->stream[stream].page_table); + if (ret < 0) { + dev_err(sdev->dev, "error: can't alloc page table for %s %d\n", + caps->name, ret); + snd_dma_free_pages(&spcm->stream[stream].page_table); + return ret; + } + + /* TODO: assign channel maps from topology */ + + return ret; +} + +static void sof_pcm_free(struct snd_pcm *pcm) +{ + struct snd_sof_pcm *spcm; + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + + spcm = snd_sof_find_spcm_dai(sdev, rtd); + if (!spcm) { + dev_warn(sdev->dev, "warn: can't find PCM with DAI ID %d\n", + rtd->dai_link->id); + return; + } + + if (spcm->pcm.playback) + snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].page_table); + + if (spcm->pcm.capture) + snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table); + + snd_sof_free_topology(sdev); +} + +static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_dai *dai = + snd_sof_find_dai(sdev, (char *)rtd->dai_link->name); + + if (!dai) { + dev_err(sdev->dev, "No DAI is found!\n"); + + /* set 48k, stereo, 16bits by default */ + rate->min = 48000; + rate->max = 48000; + + channels->min = 2; + channels->max = 2; + + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return -EINVAL; + } + + /* read format from topology */ + snd_mask_none(fmt); + + switch (dai->comp_dai.config.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + break; + default: + dev_err(sdev->dev, "No available DAI format!\n"); + return -EINVAL; + } + + /* read rate and channels from topology */ + switch (dai->dai_config.type) { + case SOF_DAI_INTEL_SSP: + rate->min = dai->dai_config.ssp.fsync_rate; + rate->max = dai->dai_config.ssp.fsync_rate; + channels->min = dai->dai_config.ssp.tdm_slots; + channels->max = dai->dai_config.ssp.tdm_slots; + + dev_dbg(sdev->dev, + "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(sdev->dev, + "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + + break; + case SOF_DAI_INTEL_DMIC: + /* fallthrough */ + case SOF_DAI_INTEL_HDA: + /* fallthrough */ + default: + dev_err(sdev->dev, "error: invalid DAI type %d\n", + dai->dai_config.type); + break; + } + + return 0; +} + +static int sof_pcm_probe(struct snd_soc_platform *platform) +{ + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(platform); + struct snd_sof_pdata *plat_data = dev_get_platdata(platform->dev); + int ret; + + /* load the default topology */ + sdev->component = &platform->component; + ret = snd_sof_load_topology(sdev, + plat_data->machine->sof_tplg_filename); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load DSP topology %d\n", + ret); + goto err; + } + + /* enable runtime PM with auto suspend */ + pm_runtime_set_autosuspend_delay(platform->dev, + SND_SOF_SUSPEND_DELAY); + pm_runtime_use_autosuspend(platform->dev); + pm_runtime_enable(platform->dev); + pm_runtime_idle(platform->dev); + +err: + return ret; +} + +static int sof_pcm_remove(struct snd_soc_platform *platform) +{ + pm_runtime_disable(platform->dev); + + return 0; +} + +void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) +{ + struct snd_soc_platform_driver *pd = &sdev->plat_drv; + struct snd_sof_pdata *plat_data = sdev->pdata; + + dev_dbg(sdev->dev, "using platform alias %s\n", + plat_data->machine->asoc_plat_name); + + pd->probe = sof_pcm_probe; + pd->remove = sof_pcm_remove; + pd->ops = &sof_pcm_ops; + pd->compr_ops = &sof_compressed_ops; + pd->pcm_new = sof_pcm_new; + pd->pcm_free = sof_pcm_free; + pd->ignore_machine = plat_data->machine->drv_name; + pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; + pd->be_pcm_base = SOF_BE_PCM_BASE; + pd->use_dai_pcm_id = true; + pd->topology_name_prefix = "sof"; +} + +static const struct snd_soc_dai_ops sof_dai_ops = { +}; + +static const struct snd_soc_component_driver sof_dai_component = { + .name = "sof-dai", +}; + +#define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) + +void snd_sof_new_dai_drv(struct snd_sof_dev *sdev) +{ + struct snd_soc_dai_driver *dd = &sdev->dai_drv; + //struct snd_sof_pdata *plat_data = sdev->pdata; + + sdev->cmpnt_drv = &sof_dai_component; + dd->playback.channels_min = 1; + dd->playback.channels_max = 16; + dd->playback.rates = SNDRV_PCM_RATE_8000_192000; + dd->playback.formats = SOF_FORMATS; + dd->capture.channels_min = 1; + dd->capture.channels_max = 16; + dd->capture.rates = SNDRV_PCM_RATE_8000_192000; + dd->capture.formats = SOF_FORMATS; + dd->ops = &sof_dai_ops; + sdev->num_dai = 1; +} From 95545bc033178c58ea641e67ce5c873916caa6e2 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:33:05 +0000 Subject: [PATCH 111/285] ASoC: SOF: Add support for loading topologies SOF uses topology to define the DAPM graphs and widgets, DAIs, PCMs and set parameters for init and run time usage. This patch loads topology and maps it to IPC commands that are build the topology on the DSP. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-topology.h | 76 ++ sound/soc/sof/sof-priv.h | 1 + sound/soc/sof/topology.c | 1825 +++++++++++++++++++++++++++++ 3 files changed, 1902 insertions(+) create mode 100644 include/uapi/sound/sof-topology.h create mode 100644 sound/soc/sof/topology.c diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h new file mode 100644 index 00000000000000..62c3f242509701 --- /dev/null +++ b/include/uapi/sound/sof-topology.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Keyon Jie + */ + +/* + * Topology IDs and tokens. + * + * ** MUST BE ALIGNED WITH TOPOLOGY CONFIGURATION TOKEN VALUES ** + */ + +#ifndef __INCLUDE_UAPI_SOF_TOPOLGOY_H__ +#define __INCLUDE_UAPI_SOF_TOPOLOGY_H__ + +/* + * Kcontrol IDs + */ +#define SOF_TPLG_KCTL_VOL_ID 256 +#define SOF_TPLG_KCTL_ENUM_ID 257 +#define SOF_TPLG_KCTL_BYTES_ID 258 + +/* + * Tokens - must match values in topology configurations + */ + +/* buffers */ +#define SOF_TKN_BUF_SIZE 100 +#define SOF_TKN_BUF_CAPS 101 + +/* DAI */ +#define SOF_TKN_DAI_DMAC 151 +#define SOF_TKN_DAI_DMAC_CHAN 152 +#define SOF_TKN_DAI_DMAC_CONFIG 153 +#define SOF_TKN_DAI_TYPE 154 +#define SOF_TKN_DAI_INDEX 155 +#define SOF_TKN_DAI_SAMPLE_BITS 156 + +/* scheduling */ +#define SOF_TKN_SCHED_DEADLINE 200 +#define SOF_TKN_SCHED_PRIORITY 201 +#define SOF_TKN_SCHED_MIPS 202 +#define SOF_TKN_SCHED_CORE 203 +#define SOF_TKN_SCHED_FRAMES 204 +#define SOF_TKN_SCHED_TIMER 205 + +/* volume */ +#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250 +#define SOF_TKN_VOLUME_RAMP_STEP_MS 251 + +/* SRC */ +#define SOF_TKN_SRC_RATE_IN 300 +#define SOF_TKN_SRC_RATE_OUT 301 + +/* PCM */ +#define SOF_TKN_PCM_DMAC 351 +#define SOF_TKN_PCM_DMAC_CHAN 352 +#define SOF_TKN_PCM_DMAC_CONFIG 353 + +/* Generic components */ +#define SOF_TKN_COMP_PERIOD_SINK_COUNT 400 +#define SOF_TKN_COMP_PERIOD_SOURCE_COUNT 401 +#define SOF_TKN_COMP_FORMAT 402 +#define SOF_TKN_COMP_PRELOAD_COUNT 403 + +/* SSP */ +#define SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE 500 +#define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 +#define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 + +#endif diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0f9b659461d94e..bde985f6f438be 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -185,6 +185,7 @@ struct snd_sof_control { struct sof_ipc_ctrl_data *control_data; u32 size; /* cdata size */ enum sof_ipc_ctrl_cmd cmd; + u32 *volume_table; /* volume table computed from tlv data*/ struct mutex mutex; struct list_head list; /* list in sdev control list */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c new file mode 100644 index 00000000000000..5fe74fa41e423f --- /dev/null +++ b/sound/soc/sof/topology.c @@ -0,0 +1,1825 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +#define COMP_ID_UNASSIGNED 0xffffffff +/* Constants used in the computation of linear volume gain from dB gain */ +/* 20th root of 10 in Q1.16 fixed-point notation*/ +#define VOL_TWENTIETH_ROOT_OF_TEN 73533 +/* 40th root of 10 in Q1.16 fixed-point notation*/ +#define VOL_FORTIETH_ROOT_OF_TEN 69419 +/* Volume fractional word length */ +#define VOLUME_FWL 16 +/* 0.5 dB step value in topology TLV */ +#define VOL_HALF_DB_STEP 50 + +/* TLV data items */ +#define TLV_ITEMS 3 +#define TLV_MIN 0 +#define TLV_STEP 1 +#define TLV_MUTE 2 + +static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS]) +{ + /* we only support dB scale TLV type at the moment */ + if ((int)p[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE) + return -EINVAL; + + /* min value in topology tlv data is multiplied by 100 */ + tlv[TLV_MIN] = (int)p[SNDRV_CTL_TLVO_DB_SCALE_MIN] / 100; + + /* volume steps */ + tlv[TLV_STEP] = (int)(p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & + TLV_DB_SCALE_MASK); + + /* mute ON/OFF */ + if ((p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & + TLV_DB_SCALE_MUTE) == 0) + tlv[TLV_MUTE] = 0; + else + tlv[TLV_MUTE] = 1; + + return 0; +} + +/* Function to truncate an unsigned 64-bit number + * by x bits and return 32-bit unsigned number + * This function also takes care of rounding while truncating + */ +static inline u32 vol_shift_64(u64 i, u32 x) +{ + /* do not truncate more than 32 bits */ + if (x > 32) + x = 32; + + if (x == 0) + return (u32)i; + + return (u32)(((i >> (x - 1)) + 1) >> 1); +} + +/* Function to compute a ^ exp where, + * a is a fractional number represented by a fixed-point integer + * with a fractional world length of "fwl" + * exp is an integer + * fwl is the fractional word length + * Return value is a fractional number represented by a fixed-point + * integer with a fractional word length of "fwl" + */ +static u32 vol_pow32(u32 a, int exp, u32 fwl) +{ + int i, iter; + u32 power = 1 << fwl; + u64 numerator; + + /* if exponent is 0, return 1 */ + if (exp == 0) + return power; + + /* determine the number of iterations based on the exponent */ + if (exp < 0) + iter = exp * -1; + else + iter = exp; + + /* mutiply a "iter" times to compute power */ + for (i = 0; i < iter; i++) { + /* Product of 2 Qx.fwl fixed-point numbers yields a Q2*x.2*fwl + * Truncate product back to fwl fractional bits with rounding + */ + power = vol_shift_64((u64)power * a, fwl); + } + + if (exp > 0) { + /* if exp is positive, return the result */ + return power; + } + + /* if exp is negative, return the multiplicative inverse */ + numerator = (u64)1 << (fwl << 1); + do_div(numerator, power); + + return (u32)numerator; +} + +/* Function to calculate volume gain from TLV data + * This function can only handle gain steps that are multiples of 0.5 dB + */ +static u32 vol_compute_gain(u32 value, int *tlv) +{ + int dB_gain; + u32 linear_gain; + int f_step; + + /* mute volume */ + if (value == 0 && tlv[TLV_MUTE]) + return 0; + + /* compute dB gain from tlv + * tlv_step in topology is multiplied by 100 + */ + dB_gain = tlv[TLV_MIN] + (value * tlv[TLV_STEP]) / 100; + + /* compute linear gain + * represented by fixed-point int with VOLUME_FWL fractional bits + */ + linear_gain = vol_pow32(VOL_TWENTIETH_ROOT_OF_TEN, dB_gain, VOLUME_FWL); + + /* extract the fractional part of volume step */ + f_step = tlv[TLV_STEP] - (tlv[TLV_STEP] / 100); + + /* if volume step is an odd multiple of 0.5 dB */ + if (f_step == VOL_HALF_DB_STEP && (value & 1)) + linear_gain = vol_shift_64((u64)linear_gain * + VOL_FORTIETH_ROOT_OF_TEN, + VOLUME_FWL); + + return linear_gain; +} + +/* Set up volume table for kcontrols from tlv data + * "size" specifies the number of entries in the table + */ +static int set_up_volume_table(struct snd_sof_control *scontrol, + int tlv[TLV_ITEMS], int size) +{ + int j; + + /* init the volume table */ + scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); + if (!scontrol->volume_table) + return -ENOMEM; + + /* populate the volume table */ + for (j = 0; j < size ; j++) + scontrol->volume_table[j] = vol_compute_gain(j, tlv); + + return 0; +} + +struct sof_dai_types { + const char *name; + enum sof_ipc_dai_type type; +}; + +static const struct sof_dai_types sof_dais[] = { + {"SSP", SOF_DAI_INTEL_SSP}, + {"HDA", SOF_DAI_INTEL_HDA}, + {"DMIC", SOF_DAI_INTEL_DMIC}, +}; + +static enum sof_ipc_dai_type find_dai(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_dais); i++) { + if (strcmp(name, sof_dais[i].name) == 0) + return sof_dais[i].type; + } + + return SOF_DAI_INTEL_NONE; +} + +struct sof_frame_types { + const char *name; + enum sof_ipc_frame frame; +}; + +static const struct sof_frame_types sof_frames[] = { + {"s16le", SOF_IPC_FRAME_S16_LE}, + {"s24le", SOF_IPC_FRAME_S24_4LE}, + {"s32le", SOF_IPC_FRAME_S32_LE}, + {"float", SOF_IPC_FRAME_FLOAT}, +}; + +static enum sof_ipc_dai_type find_format(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_frames); i++) { + if (strcmp(name, sof_frames[i].name) == 0) + return sof_frames[i].frame; + } + + /* use s32le if nothing is specified */ + return SOF_IPC_FRAME_S32_LE; +} + +/* + * Standard Kcontrols. + */ + +static int sof_control_load_volume(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_mixer_control *mc = + (struct snd_soc_tplg_mixer_control *)hdr; + struct sof_ipc_ctrl_data *cdata; + + /* validate topology data */ + if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) + return -EINVAL; + + /* init the volume get/put data */ + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + + sizeof(struct sof_ipc_ctrl_value_chan) * mc->num_channels; + scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); + cdata = scontrol->control_data; + if (!scontrol->control_data) + return -ENOMEM; + + scontrol->comp_id = sdev->next_comp_id; + scontrol->num_channels = mc->num_channels; + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + scontrol->comp_id, scontrol->num_channels); + + return 0; + /* configure channel IDs */ + //for (i = 0; i < mc->num_channels; i++) { + // v.pcm.chmap[i] = mc->channel[i].id; + //} +} + +struct sof_topology_token { + u32 token; + u32 type; + int (*get_token)(void *elem, void *object, u32 offset, u32 size); + u32 offset; + u32 size; +}; + +static int get_token_u32(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + u32 *val = object + offset; + + *val = velem->value; + return 0; +} + +static int get_token_comp_format(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_string_elem *velem = elem; + u32 *val = object + offset; + + *val = find_format(velem->string); + return 0; +} + +static int get_token_dai_type(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_string_elem *velem = elem; + u32 *val = object + offset; + + *val = find_dai(velem->string); + return 0; +} + +/* Buffers */ +static const struct sof_topology_token buffer_tokens[] = { + {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_buffer, size), 0}, + {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_buffer, caps), 0}, +}; + +/* DAI */ +static const struct sof_topology_token dai_tokens[] = { + {SOF_TKN_DAI_DMAC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dmac_id), 0}, + {SOF_TKN_DAI_DMAC_CHAN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dmac_chan), 0}, + {SOF_TKN_DAI_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dmac_config), 0}, + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_comp_dai, type), 0}, + {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, index), 0}, +}; + +/* BE DAI link */ +static const struct sof_topology_token dai_link_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_dai_config, type), 0}, +}; + +static const struct sof_topology_token dai_ssp_link_tokens[] = { + {SOF_TKN_DAI_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, +}; + +/* scheduling */ +static const struct sof_topology_token sched_tokens[] = { + {SOF_TKN_SCHED_DEADLINE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, deadline), 0}, + {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, priority), 0}, + {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, mips), 0}, + {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, core), 0}, + {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, frames_per_sched), 0}, + {SOF_TKN_SCHED_TIMER, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, timer), 0}, +}; + +/* volume */ +static const struct sof_topology_token volume_tokens[] = { + {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, offsetof(struct sof_ipc_comp_volume, ramp), 0}, + {SOF_TKN_VOLUME_RAMP_STEP_MS, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_volume, initial_ramp), 0}, +}; + +/* SRC */ +static const struct sof_topology_token src_tokens[] = { + {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, source_rate), 0}, + {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, sink_rate), 0}, +}; + +/* Tone */ +static const struct sof_topology_token tone_tokens[] = { +}; + +/* PCM */ +static const struct sof_topology_token pcm_tokens[] = { + {SOF_TKN_PCM_DMAC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_host, dmac_id), 0}, + {SOF_TKN_PCM_DMAC_CHAN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_host, dmac_chan), 0}, + {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_host, dmac_config), 0}, +}; + +/* Generic components */ +static const struct sof_topology_token comp_tokens[] = { + {SOF_TKN_COMP_PERIOD_SINK_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_sink), 0}, + {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_source), 0}, + {SOF_TKN_COMP_FORMAT, + SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_comp_config, frame_fmt), 0}, + {SOF_TKN_COMP_PRELOAD_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, preload_count), 0}, +}; + +/* SSP */ +static const struct sof_topology_token ssp_tokens[] = { + {SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, mclk_keep_active), 0}, + {SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, bclk_keep_active), 0}, + {SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, fs_keep_active), 0}, +}; + +/* DMIC */ +static const struct sof_topology_token dmic_tokens[] = { +}; + +/* HDA */ +static const struct sof_topology_token hda_tokens[] = { +}; + +static void sof_parse_uuid_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_uuid_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < array->num_elems; i++) { + elem = &array->uuid[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID) + continue; + + /* match token id */ + if (tokens[j].token != elem->token) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +static void sof_parse_string_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_string_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < array->num_elems; i++) { + elem = &array->string[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING) + continue; + + /* match token id */ + if (tokens[j].token != elem->token) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +static void sof_parse_word_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_value_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < array->num_elems; i++) { + elem = &array->value[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_WORD) + continue; + + /* match token id */ + if (tokens[j].token != elem->token) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +static int sof_parse_tokens(struct snd_soc_component *scomp, + void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array, + int priv_size) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + int asize; + + while (priv_size > 0) { + asize = array->size; + + /* validate asize */ + if (asize < 0) { /* FIXME: A zero-size array makes no sense */ + dev_err(sdev->dev, "error: invalid array size 0x%x\n", + asize); + return -EINVAL; + } + + /* make sure there is enough data before parsing */ + priv_size -= asize; + if (priv_size < 0) { + dev_err(sdev->dev, "error: invalid array size 0x%x\n", + asize); + return -EINVAL; + } + + /* call correct parser depending on type */ + switch (array->type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + sof_parse_uuid_tokens(scomp, object, tokens, count, + array); + break; + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + sof_parse_string_tokens(scomp, object, tokens, count, + array); + break; + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + sof_parse_word_tokens(scomp, object, tokens, count, + array); + break; + default: + dev_err(sdev->dev, "error: unknown token type %d\n", + array->type); + return -EINVAL; + } + + /* next array */ + array = (void *)array + asize; + } + return 0; +} + +static void sof_dbg_comp_config(struct snd_soc_component *scomp, + struct sof_ipc_comp_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + + dev_dbg(sdev->dev, " config: periods snk %d src %d fmt %d\n", + config->periods_sink, config->periods_source, + config->frame_fmt); +} + +/* external kcontrol init - used for any driver specific init */ +static int sof_control_load(struct snd_soc_component *scomp, int index, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct soc_mixer_control *sm; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_dobj *dobj = NULL; + struct snd_sof_control *scontrol; + int ret = -EINVAL; + + dev_dbg(sdev->dev, "tplg: load control type %d name : %s\n", + hdr->type, hdr->name); + + scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL); + if (!scontrol) + return -ENOMEM; + + scontrol->sdev = sdev; + mutex_init(&scontrol->mutex); + + switch (hdr->ops.info) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + sm = (struct soc_mixer_control *)kc->private_value; + dobj = &sm->dobj; + ret = sof_control_load_volume(scomp, scontrol, kc, hdr); + break; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_BYTES: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_CTL_RANGE: + case SND_SOC_TPLG_CTL_STROBE: + case SND_SOC_TPLG_DAPM_CTL_VOLSW: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_PIN: + default: + dev_warn(sdev->dev, "control type not supported %d:%d:%d\n", + hdr->ops.get, hdr->ops.put, hdr->ops.info); + return 0; + } + + dobj->private = scontrol; + list_add(&scontrol->list, &sdev->kcontrol_list); + return ret; +} + +static int sof_control_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_free fcomp; + struct snd_sof_control *scontrol = dobj->private; + + dev_dbg(sdev->dev, "tplg: unload control name : %s\n", scomp->name); + + fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; + fcomp.hdr.size = sizeof(fcomp); + fcomp.id = scontrol->comp_id; + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, + fcomp.hdr.cmd, &fcomp, sizeof(fcomp), + NULL, 0); +} + +/* + * DAI Topology + */ + +static int sof_connect_dai_widget(struct snd_soc_component *scomp, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw, + struct snd_sof_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_card *card = scomp->card; + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + dev_dbg(sdev->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", + w->name, w->sname, rtd->dai_link->stream_name); + + if (!w->sname || !rtd->dai_link->stream_name) + continue; + + /* does stream match DAI link ? */ + if (strcmp(w->sname, rtd->dai_link->stream_name)) + continue; + + switch (w->id) { + case snd_soc_dapm_dai_out: + rtd->cpu_dai->capture_widget = w; + if (dai) + dai->name = rtd->dai_link->name; + dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n", + w->name, rtd->dai_link->name); + break; + case snd_soc_dapm_dai_in: + rtd->cpu_dai->playback_widget = w; + if (dai) + dai->name = rtd->dai_link->name; + dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n", + w->name, rtd->dai_link->name); + break; + default: + break; + } + } + + /* check we have a connection */ + if (!dai->name) { + dev_err(sdev->dev, "error: can't connect DAI %s stream %s\n", + w->name, w->sname); + return -EINVAL; + } + + return 0; +} + +static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r, + struct snd_sof_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_dai comp_dai; + int ret; + + /* configure dai IPC message */ + memset(&comp_dai, 0, sizeof(comp_dai)); + comp_dai.comp.hdr.size = sizeof(comp_dai); + comp_dai.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + comp_dai.comp.id = swidget->comp_id; + comp_dai.comp.type = SOF_COMP_DAI; + comp_dai.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &comp_dai, dai_tokens, + ARRAY_SIZE(dai_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dai tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, &comp_dai.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dai.cfg tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "dai %s: dmac %d chan %d type %d index %d\n", + swidget->widget->name, comp_dai.dmac_id, comp_dai.dmac_chan, + comp_dai.type, comp_dai.index); + sof_dbg_comp_config(scomp, &comp_dai.config); + + ret = sof_ipc_tx_message(sdev->ipc, comp_dai.comp.hdr.cmd, + &comp_dai, sizeof(comp_dai), r, sizeof(*r)); + + if (ret == 0 && dai) { + dai->sdev = sdev; + memcpy(&dai->comp_dai, &comp_dai, sizeof(comp_dai)); + } + + return ret; +} + +/* + * Buffer topology + */ + +static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_buffer buffer; + int ret; + + /* configure dai IPC message */ + memset(&buffer, 0, sizeof(buffer)); + buffer.comp.hdr.size = sizeof(buffer); + buffer.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; + buffer.comp.id = swidget->comp_id; + buffer.comp.type = SOF_COMP_BUFFER; + buffer.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &buffer, buffer_tokens, + ARRAY_SIZE(buffer_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse buffer tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "buffer %s: size %d caps 0x%x\n", + swidget->widget->name, buffer.size, buffer.caps); + + return sof_ipc_tx_message(sdev->ipc, + buffer.comp.hdr.cmd, &buffer, sizeof(buffer), + r, sizeof(*r)); +} + +/* + * PCM Topology + */ + +static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + enum sof_ipc_stream_direction dir, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_host host; + int ret; + + /* configure mixer IPC message */ + memset(&host, 0, sizeof(host)); + host.comp.hdr.size = sizeof(host); + host.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + host.comp.id = swidget->comp_id; + host.comp.type = SOF_COMP_HOST; + host.comp.pipeline_id = index; + host.direction = dir; + + ret = sof_parse_tokens(scomp, &host, pcm_tokens, + ARRAY_SIZE(pcm_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse host tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, &host.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse host.cfg tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "host %s: dmac %d chan %d\n", + swidget->widget->name, host.dmac_id, host.dmac_chan); + sof_dbg_comp_config(scomp, &host.config); + + return sof_ipc_tx_message(sdev->ipc, + host.comp.hdr.cmd, &host, sizeof(host), r, + sizeof(*r)); +} + +/* + * Pipeline Topology + */ + +static int sof_widget_load_pipeline(struct snd_soc_component *scomp, + int index, struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_pipe_new pipeline; + struct snd_sof_widget *comp_swidget; + int ret; + + /* configure dai IPC message */ + memset(&pipeline, 0, sizeof(pipeline)); + pipeline.hdr.size = sizeof(pipeline); + pipeline.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; + pipeline.pipeline_id = index; + pipeline.comp_id = swidget->comp_id; + + /* component at start of pipeline is our stream id */ + comp_swidget = snd_sof_find_swidget(sdev, tw->sname); + if (!comp_swidget) { + dev_err(sdev->dev, "error: widget %s refers to non existent widget %s\n", + tw->name, tw->sname); + return -EINVAL; + } + + pipeline.sched_id = comp_swidget->comp_id; + + dev_dbg(sdev->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n", + pipeline.pipeline_id, pipeline.comp_id, pipeline.sched_id); + + ret = sof_parse_tokens(scomp, &pipeline, sched_tokens, + ARRAY_SIZE(sched_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse pipeline tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "pipeline %s: deadline %d pri %d mips %d core %d frames %d\n", + swidget->widget->name, pipeline.deadline, pipeline.priority, + pipeline.mips, pipeline.core, pipeline.frames_per_sched); + + return sof_ipc_tx_message(sdev->ipc, + pipeline.hdr.cmd, &pipeline, sizeof(pipeline), + r, sizeof(*r)); +} + +/* + * Mixer topology + */ + +static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_mixer mixer; + int ret; + + /* configure mixer IPC message */ + memset(&mixer, 0, sizeof(mixer)); + mixer.comp.hdr.size = sizeof(mixer); + mixer.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + mixer.comp.id = swidget->comp_id; + mixer.comp.type = SOF_COMP_MIXER; + mixer.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &mixer.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse mixer.cfg tokens failed %d\n", + private->size); + return ret; + } + + sof_dbg_comp_config(scomp, &mixer.config); + return sof_ipc_tx_message(sdev->ipc, + mixer.comp.hdr.cmd, &mixer, sizeof(mixer), r, + sizeof(*r)); +} + +/* + * PGA Topology + */ + +static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_volume volume; + struct snd_soc_dapm_widget *widget = swidget->widget; + const struct snd_kcontrol_new *kc = NULL; + struct soc_mixer_control *sm; + struct snd_sof_control *scontrol; + const unsigned int *p; + int ret, tlv[TLV_ITEMS]; + + if (tw->num_kcontrols != 1) { + dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n", + tw->num_kcontrols); + return -EINVAL; + } + + /* set up volume gain tables for kcontrol */ + kc = &widget->kcontrol_news[0]; + sm = (struct soc_mixer_control *)kc->private_value; + + /* get volume control */ + scontrol = sm->dobj.private; + + /* get topology tlv data */ + p = kc->tlv.p; + + /* extract tlv data */ + if (get_tlv_data(p, tlv) < 0) { + dev_err(sdev->dev, "error: invalid TLV data\n"); + return -EINVAL; + } + + /* set up volume table */ + if (set_up_volume_table(scontrol, tlv, sm->max + 1) < 0) { + dev_err(sdev->dev, "error: setting up volume table\n"); + return -ENOMEM; + } + + /* configure dai IPC message */ + memset(&volume, 0, sizeof(volume)); + volume.comp.hdr.size = sizeof(volume); + volume.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + volume.comp.id = swidget->comp_id; + volume.comp.type = SOF_COMP_VOLUME; + volume.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &volume, volume_tokens, + ARRAY_SIZE(volume_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse volume tokens failed %d\n", + private->size); + return ret; + } + ret = sof_parse_tokens(scomp, &volume.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse volume.cfg tokens failed %d\n", + private->size); + return ret; + } + + sof_dbg_comp_config(scomp, &volume.config); + + return sof_ipc_tx_message(sdev->ipc, + volume.comp.hdr.cmd, &volume, sizeof(volume), + r, sizeof(*r)); +} + +/* + * SRC Topology + */ + +static int sof_widget_load_src(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_src src; + int ret; + + /* configure mixer IPC message */ + memset(&src, 0, sizeof(src)); + src.comp.hdr.size = sizeof(src); + src.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + src.comp.id = swidget->comp_id; + src.comp.type = SOF_COMP_SRC; + src.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &src, src_tokens, + ARRAY_SIZE(src_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse src tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, &src.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse src.cfg tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "src %s: source rate %d sink rate %d\n", + swidget->widget->name, src.source_rate, src.sink_rate); + sof_dbg_comp_config(scomp, &src.config); + + return sof_ipc_tx_message(sdev->ipc, + src.comp.hdr.cmd, &src, sizeof(src), r, + sizeof(*r)); +} + +/* + * Signal Generator Topology + */ + +static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &tw->priv; + struct sof_ipc_comp_tone tone; + int ret; + + /* configure mixer IPC message */ + memset(&tone, 0, sizeof(tone)); + tone.comp.hdr.size = sizeof(tone); + tone.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + tone.comp.id = swidget->comp_id; + tone.comp.type = SOF_COMP_TONE; + tone.comp.pipeline_id = index; + + ret = sof_parse_tokens(scomp, &tone, tone_tokens, + ARRAY_SIZE(tone_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse tone tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, &tone.config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse tone.cfg tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "tone %s: frequency %d amplitude %d\n", + swidget->widget->name, tone.frequency, tone.amplitude); + sof_dbg_comp_config(scomp, &tone.config); + + return sof_ipc_tx_message(sdev->ipc, + tone.comp.hdr.cmd, &tone, sizeof(tone), r, + sizeof(*r)); +} + +/* + * Generic widget loader. + */ + +static int sof_widget_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw) +{ + return 0; +} + +/* external widget init - used for any driver specific init */ +static int sof_widget_ready(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tw) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + struct snd_sof_dai *dai; + struct sof_ipc_comp_reply reply; + struct snd_sof_control *scontrol = NULL; + int ret = 0; + + swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); + if (!swidget) + return -ENOMEM; + + swidget->sdev = sdev; + swidget->widget = w; + swidget->comp_id = sdev->next_comp_id++; + swidget->complete = 0; + swidget->id = w->id; + swidget->pipeline_id = index; + swidget->private = NULL; + memset(&reply, 0, sizeof(reply)); + + dev_dbg(sdev->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n", + swidget->comp_id, index, swidget->id, tw->name, + tw->sname ? tw->sname : "none"); + + /* handle any special case widgets */ + switch (w->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + dai = kzalloc(sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + ret = sof_widget_load_dai(scomp, index, swidget, tw, &reply, + dai); + if (ret == 0) { + sof_connect_dai_widget(scomp, w, tw, dai); + list_add(&dai->list, &sdev->dai_list); + swidget->private = dai; + } else { + kfree(dai); + } + break; + case snd_soc_dapm_mixer: + ret = sof_widget_load_mixer(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_pga: + ret = sof_widget_load_pga(scomp, index, swidget, tw, &reply); + /* Find scontrol for this pga and set readback offset*/ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + if (scontrol->comp_id == swidget->comp_id) { + scontrol->readback_offset = reply.offset; + break; + } + } + break; + case snd_soc_dapm_buffer: + ret = sof_widget_load_buffer(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_scheduler: + ret = sof_widget_load_pipeline(scomp, index, swidget, tw, + &reply); + break; + case snd_soc_dapm_aif_out: + ret = sof_widget_load_pcm(scomp, index, swidget, + SOF_IPC_STREAM_CAPTURE, tw, &reply); + break; + case snd_soc_dapm_aif_in: + ret = sof_widget_load_pcm(scomp, index, swidget, + SOF_IPC_STREAM_PLAYBACK, tw, &reply); + break; + case snd_soc_dapm_src: + ret = sof_widget_load_src(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_siggen: + ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); + break; + case snd_soc_dapm_mux: + case snd_soc_dapm_demux: + case snd_soc_dapm_switch: + case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: + case snd_soc_dapm_effect: + default: + dev_warn(sdev->dev, "warning: widget type %d name %s not handled\n", + swidget->id, tw->name); + break; + } + + /* check IPC reply */ + if (ret < 0 || reply.rhdr.error < 0) { + dev_err(sdev->dev, + "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n", + tw->shift, swidget->id, tw->name, + tw->sname ? tw->sname : "none", reply.rhdr.error); + return ret; + } + + w->dobj.private = swidget; + mutex_init(&swidget->mutex); + list_add(&swidget->list, &sdev->widget_list); + return ret; +} + +static int sof_widget_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_sof_widget *swidget; + struct snd_sof_dai *dai; + + swidget = dobj->private; + if (!swidget) + return 0; + + dai = swidget->private; + + /* remove and free dai object */ + if (dai) { + list_del(&dai->list); + kfree(dai); + } + + /* remove and free swidget object */ + list_del(&swidget->list); + kfree(swidget); + + return 0; +} + +/* FE DAI - used for any driver specific init */ +static int sof_dai_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pcm *spcm; + + /* don't need to do anything for BEs atm */ + if (!pcm) + return 0; + + spcm = kzalloc(sizeof(*spcm), GFP_KERNEL); + if (!spcm) + return -ENOMEM; + + spcm->sdev = sdev; + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = COMP_ID_UNASSIGNED; + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = COMP_ID_UNASSIGNED; + if (pcm) { + spcm->pcm = *pcm; + dev_dbg(sdev->dev, "tplg: load pcm %s\n", pcm->dai_name); + } + dai_drv->dobj.private = spcm; + mutex_init(&spcm->mutex); + list_add(&spcm->list, &sdev->pcm_list); + + return 0; +} + +static int sof_dai_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_sof_pcm *spcm = dobj->private; + + list_del(&spcm->list); + kfree(spcm); + + return 0; +} + +static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + /* clock directions wrt codec */ + if (hw_config->bclk_master == SND_SOC_TPLG_BCLK_CM) { + /* codec is bclk master */ + if (hw_config->fsync_master == SND_SOC_TPLG_FSYNC_CM) + config->format |= SOF_DAI_FMT_CBM_CFM; + else + config->format |= SOF_DAI_FMT_CBM_CFS; + } else { + /* codec is bclk slave */ + if (hw_config->fsync_master == SND_SOC_TPLG_FSYNC_CM) + config->format |= SOF_DAI_FMT_CBS_CFM; + else + config->format |= SOF_DAI_FMT_CBS_CFS; + } + + /* inverted clocks ? */ + if (hw_config->invert_bclk) { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_IB_IF; + else + config->format |= SOF_DAI_FMT_IB_NF; + } else { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_NB_IF; + else + config->format |= SOF_DAI_FMT_NB_NF; + } +} + +static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->ssp, 0, sizeof(struct sof_ipc_dai_ssp_params)); + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, &config->ssp, dai_ssp_link_tokens, + ARRAY_SIZE(dai_ssp_link_tokens), + private->array, private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse ssp link tokens failed %d\n", + private->size); + return ret; + } + + ret = sof_parse_tokens(scomp, config, ssp_tokens, + ARRAY_SIZE(ssp_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse ssp tokens failed %d\n", + private->size); + return ret; + } + + config->ssp.mclk_rate = hw_config->mclk_rate; + config->ssp.bclk_rate = hw_config->bclk_rate; + config->ssp.fsync_rate = hw_config->fsync_rate; + config->ssp.tdm_slots = hw_config->tdm_slots; + config->ssp.tdm_slot_width = hw_config->tdm_slot_width; + config->ssp.mclk_direction = hw_config->mclk_direction; + config->ssp.rx_slots = hw_config->rx_slots; + config->ssp.tx_slots = hw_config->tx_slots; + + dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d\n", + config->id, config->format, + config->ssp.mclk_rate, config->ssp.bclk_rate, + config->ssp.fsync_rate, config->ssp.sample_valid_bits, + config->ssp.tdm_slot_width, config->ssp.tdm_slots); + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set DAI config for SSP%d\n", + config->id); + + return ret; +} + +static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* init IPC */ + memset(&config->dmic, 0, sizeof(struct sof_ipc_dai_dmic_params)); + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, config, dmic_tokens, + ARRAY_SIZE(dmic_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dmic tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "tplg: config DMIC%d fmt 0x%x\n", + config->id, config->format); + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set DAI config for DMIC%d\n", + config->id); + + return ret; +} + +static int sof_link_hda_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* init IPC */ + memset(&config->hda, 0, sizeof(struct sof_ipc_dai_hda_params)); + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, config, hda_tokens, + ARRAY_SIZE(hda_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse hda tokens failed %d\n", + private->size); + return ret; + } + + dev_dbg(sdev->dev, "tplg: config HDA%d fmt 0x%x\n", + config->id, config->format); + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set DAI config for HDA%d\n", + config->id); + + return ret; +} + +/* DAI link - used for any driver specific init */ +static int sof_link_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_dai_config config; + struct snd_soc_tplg_hw_config *hw_config; + struct snd_sof_dai *dai; + int ret = 0; + + link->platform_name = "sof-audio"; + link->nonatomic = true; + + /* send BE configurations to DSP */ + if (!link->no_pcm) + return 0; + + /* only support 1 config atm */ + if (cfg->num_hw_configs != 1) { + dev_err(sdev->dev, "error: unexpected DAI config count %d\n", + cfg->num_hw_configs); + return -EINVAL; + } + + /* check we have some tokens - we need at least DAI type */ + if (private->size == 0) { + dev_err(sdev->dev, "error: expected tokens for DAI, none found\n"); + return -EINVAL; + } + + memset(&config, 0, sizeof(config)); + + /* get any common DAI tokens */ + ret = sof_parse_tokens(scomp, &config, dai_link_tokens, + ARRAY_SIZE(dai_link_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse link tokens failed %d\n", + private->size); + return ret; + } + + /* configure dai IPC message */ + hw_config = &cfg->hw_config[0]; + + config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config.id = hw_config->id; + config.format = hw_config->fmt; + + /* now load DAI specific data and send IPC - type comes from token */ + switch (config.type) { + case SOF_DAI_INTEL_SSP: + ret = sof_link_ssp_load(scomp, index, link, cfg, hw_config, + &config); + break; + case SOF_DAI_INTEL_DMIC: + ret = sof_link_dmic_load(scomp, index, link, cfg, hw_config, + &config); + break; + case SOF_DAI_INTEL_HDA: + ret = sof_link_hda_load(scomp, index, link, cfg, hw_config, + &config); + break; + default: + dev_err(sdev->dev, "error: invalid DAI type %d\n", config.type); + ret = -EINVAL; + break; + } + + dai = snd_sof_find_dai(sdev, (char *)link->name); + if (dai) + memcpy(&dai->dai_config, &config, + sizeof(struct sof_ipc_dai_config)); + + return 0; +} + +static int sof_link_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + return 0; +} + +/* bind PCM ID to host component ID */ +static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, + const char *host) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *host_widget; + + host_widget = snd_sof_find_swidget(sdev, (char *)host); + if (!host_widget) { + dev_err(sdev->dev, "error: can't find host component %s\n", + host); + return -ENODEV; + } + + switch (host_widget->id) { + case snd_soc_dapm_aif_in: + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = + host_widget->comp_id; + break; + case snd_soc_dapm_aif_out: + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = + host_widget->comp_id; + break; + default: + dev_err(sdev->dev, "error: host is wrong type %d\n", + host_widget->id); + return -EINVAL; + } + + return 0; +} + +/* DAI link - used for any driver specific init */ +static int sof_route_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_route *route) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_pipe_comp_connect connect; + struct snd_sof_widget *source_swidget, *sink_swidget; + struct snd_sof_pcm *spcm; + struct sof_ipc_reply reply; + int ret; + + memset(&connect, 0, sizeof(connect)); + connect.hdr.size = sizeof(connect); + connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; + + dev_dbg(sdev->dev, "sink %s control %s source %s\n", + route->sink, route->control ? route->control : "none", + route->source); + + /* source component */ + source_swidget = snd_sof_find_swidget(sdev, (char *)route->source); + if (!source_swidget) { + /* don't send any routes to DSP that include a driver PCM */ + spcm = snd_sof_find_spcm_name(sdev, (char *)route->source); + if (spcm) + return spcm_bind(scomp, spcm, route->sink); + + dev_err(sdev->dev, "error: source %s not found\n", + route->source); + return -EINVAL; + } + + connect.source_id = source_swidget->comp_id; + + /* sink component */ + sink_swidget = snd_sof_find_swidget(sdev, (char *)route->sink); + if (!sink_swidget) { + /* don't send any routes to DSP that include a driver PCM */ + spcm = snd_sof_find_spcm_name(sdev, (char *)route->sink); + if (spcm) + return spcm_bind(scomp, spcm, route->source); + + dev_err(sdev->dev, "error: sink %s not found\n", + route->sink); + return -EINVAL; + } + + connect.sink_id = sink_swidget->comp_id; + + ret = sof_ipc_tx_message(sdev->ipc, + connect.hdr.cmd, &connect, sizeof(connect), + &reply, sizeof(reply)); + + /* check IPC return value */ + if (ret < 0) { + dev_err(sdev->dev, "error: failed to add route sink %s control %s source %s\n", + route->sink, route->control ? route->control : "none", + route->source); + return ret; + } + + /* check IPC reply */ + if (reply.error < 0) { + dev_err(sdev->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", + route->sink, route->control ? route->control : "none", + route->source, reply.error); + //return ret; // TODO: + } + + return ret; +} + +static int sof_route_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + return 0; +} + +static int sof_complete_pipeline(struct snd_soc_component *scomp, + struct snd_sof_widget *swidget) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_pipe_ready ready; + struct sof_ipc_reply reply; + int ret; + + dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", + swidget->widget->name, swidget->comp_id); + + memset(&ready, 0, sizeof(ready)); + ready.hdr.size = sizeof(ready); + ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; + ready.comp_id = swidget->comp_id; + + ret = sof_ipc_tx_message(sdev->ipc, + ready.hdr.cmd, &ready, sizeof(ready), &reply, + sizeof(reply)); + if (ret < 0) + return ret; + return 1; +} + +/* completion - called at completion of firmware loading */ +static void sof_complete(struct snd_soc_component *scomp) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + + /* some widget types require completion notificattion */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->complete) + continue; + + switch (swidget->id) { + case snd_soc_dapm_scheduler: + swidget->complete = + sof_complete_pipeline(scomp, swidget); + break; + default: + break; + } + } +} + +/* manifest - optional to inform component of manifest */ +static int sof_manifest(struct snd_soc_component *scomp, int index, + struct snd_soc_tplg_manifest *man) +{ + return 0; +} + +/* vendor specific kcontrol handlers available for binding */ +static const struct snd_soc_tplg_kcontrol_ops sof_io_ops[] = { + {SOF_TPLG_KCTL_VOL_ID, snd_sof_volume_get, snd_sof_volume_put}, + {SOF_TPLG_KCTL_ENUM_ID, snd_sof_enum_get, snd_sof_enum_put}, + {SOF_TPLG_KCTL_BYTES_ID, snd_sof_bytes_get, snd_sof_bytes_put}, +}; + +/* vendor specific bytes ext handlers available for binding */ +static const struct snd_soc_tplg_bytes_ext_ops sof_bytes_ext_ops[] = { +{}, +}; + +static struct snd_soc_tplg_ops sof_tplg_ops = { + /* external kcontrol init - used for any driver specific init */ + .control_load = sof_control_load, + .control_unload = sof_control_unload, + + /* external kcontrol init - used for any driver specific init */ + .dapm_route_load = sof_route_load, + .dapm_route_unload = sof_route_unload, + + /* external widget init - used for any driver specific init */ + .widget_load = sof_widget_load, + .widget_ready = sof_widget_ready, + .widget_unload = sof_widget_unload, + + /* FE DAI - used for any driver specific init */ + .dai_load = sof_dai_load, + .dai_unload = sof_dai_unload, + + /* DAI link - used for any driver specific init */ + .link_load = sof_link_load, + .link_unload = sof_link_unload, + + /* completion - called at completion of firmware loading */ + .complete = sof_complete, + + /* manifest - optional to inform component of manifest */ + .manifest = sof_manifest, + + /* vendor specific kcontrol handlers available for binding */ + .io_ops = sof_io_ops, + .io_ops_count = ARRAY_SIZE(sof_io_ops), + + /* vendor specific bytes ext handlers available for binding */ + .bytes_ext_ops = sof_bytes_ext_ops, + .bytes_ext_ops_count = ARRAY_SIZE(sof_bytes_ext_ops), +}; + +int snd_sof_init_topology(struct snd_sof_dev *sdev, + struct snd_soc_tplg_ops *ops) +{ + /* TODO: support linked list of topologies */ + sdev->tplg_ops = ops; + return 0; +} +EXPORT_SYMBOL(snd_sof_init_topology); + +int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file) +{ + const struct firmware *fw; + struct snd_soc_tplg_hdr *hdr; + int ret; + + dev_dbg(sdev->dev, "loading topology:%s\n", file); + + ret = request_firmware(&fw, file, sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "error: tplg %s load failed with %d\n", + file, ret); + return ret; + } + + hdr = (struct snd_soc_tplg_hdr *)fw->data; + ret = snd_soc_tplg_component_load(sdev->component, + &sof_tplg_ops, fw, + SND_SOC_TPLG_INDEX_ALL); + if (ret < 0) { + dev_err(sdev->dev, "error: tplg component load failed %d\n", + ret); + ret = -EINVAL; + } + + release_firmware(fw); + return ret; +} +EXPORT_SYMBOL(snd_sof_load_topology); + +void snd_sof_free_topology(struct snd_sof_dev *sdev) +{ + int ret; + + dev_dbg(sdev->dev, "free topology...\n"); + + ret = snd_soc_tplg_component_remove(sdev->component, + SND_SOC_TPLG_INDEX_ALL); + if (ret < 0) + dev_err(sdev->dev, + "error: tplg component free failed %d\n", ret); +} +EXPORT_SYMBOL(snd_sof_free_topology); From 496c29a4d647a5b548342877febcb94351035a20 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:33:15 +0000 Subject: [PATCH 112/285] ASoC: SOF: Add DSP firmware trace event support Add a trace event buffer that can be used by userspace to read DSP runtime trace events alongside bespoke trace data in realtime for firmware debug. Signed-off-by: Liam Girdwood --- sound/soc/sof/trace.c | 290 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 sound/soc/sof/trace.c diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c new file mode 100644 index 00000000000000..dc452ad6109359 --- /dev/null +++ b/sound/soc/sof/trace.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Yan Wang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +static int sof_wait_trace_avail(struct snd_sof_dev *sdev, size_t *count, + loff_t pos, size_t size) +{ + size_t avail; + wait_queue_entry_t wait; + + /* + * If host offset is less than local pos, it means write pointer of + * host DMA buffer has been wrapped. We should output the trace data + * at the end of host DMA buffer at first. + */ + if (sdev->host_offset < pos) { + avail = size - pos; + goto _host_end; + } + + /* If there is available trace data now, it is unnecessary to wait. */ + if (sdev->host_offset > pos) + goto _endcheck; + + /* wait for available trace data from FW */ + init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&sdev->trace_sleep, &wait); + + if (signal_pending(current)) { + remove_wait_queue(&sdev->trace_sleep, &wait); + goto _endcheck; + } + + /* set timeout to max value, no error code */ + schedule_timeout(MAX_SCHEDULE_TIMEOUT); + remove_wait_queue(&sdev->trace_sleep, &wait); + +_endcheck: + /* calculate the available count */ + avail = sdev->host_offset - pos; + +_host_end: + /* return min value between available and request count */ + *count = avail < *count ? avail : *count; + + return 0; +} + +static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + int err; + loff_t pos = *ppos; + loff_t lpos = pos; + size_t ret, size; + + size = dfse->size; + + /* check pos and count */ + if (pos < 0) + return -EINVAL; + if (!count) + return 0; + + /* + * If pos exceeds size, it means host DMA buffer has been wrapped. So + * local pos will be truncated from global pos. It is possible to wrap + * host DMA buffer multiply times when keep output long time, so we + * need one loop to process it. + */ + while (lpos >= size) + lpos -= size; + + if (count > size - lpos) + count = size - lpos; + + /* get available count based on current host offset */ + err = sof_wait_trace_avail(sdev, &count, lpos, size); + if (err < 0) { + dev_err(sdev->dev, + "error: can't get more trace %d\n", err); + return 0; + } + + /* copy available trace data to debugfs */ + ret = copy_to_user(buffer, dfse->buf + lpos, count); + + if (ret == count) + return -EFAULT; + count -= ret; + + /* move debugfs reading position */ + *ppos = pos + count; + + return count; +} + +static const struct file_operations sof_dfs_trace_fops = { + .open = simple_open, + .read = sof_dfsentry_trace_read, + .llseek = default_llseek, +}; + +static int trace_debugfs_create(struct snd_sof_dev *sdev) +{ + struct snd_sof_dfsentry *dfse; + + if (!sdev) + return -EINVAL; + + dfse = kzalloc(sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->buf = sdev->dmatb.area; + dfse->size = sdev->dmatb.bytes; + dfse->sdev = sdev; + + dfse->dfsentry = debugfs_create_file("trace", 0444, sdev->debugfs_root, + dfse, &sof_dfs_trace_fops); + if (!dfse->dfsentry) { + dev_err(sdev->dev, + "error: cannot create debugfs entry for trace\n"); + kfree(dfse); + return -ENODEV; + } + + return 0; +} + +int snd_sof_init_trace(struct snd_sof_dev *sdev) +{ + struct sof_ipc_dma_trace_params params; + struct sof_ipc_reply ipc_reply; + int ret; + + /* set false before start initialization */ + sdev->dtrace_is_enabled = false; + + /* allocate trace page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, + PAGE_SIZE, &sdev->dmatp); + if (ret < 0) { + dev_err(sdev->dev, + "error: can't alloc page table for trace %d\n", ret); + return ret; + } + + /* allocate trace data buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->parent, + DMA_BUF_SIZE_FOR_TRACE, &sdev->dmatb); + if (ret < 0) { + dev_err(sdev->dev, + "error: can't alloc buffer for trace%d\n", ret); + goto page_err; + } + + /* craete compressed page table for audio firmware */ + ret = snd_sof_create_page_table(sdev, &sdev->dmatb, sdev->dmatp.area, + sdev->dmatb.bytes); + if (ret < 0) + goto table_err; + + sdev->dma_trace_pages = ret; + dev_dbg(sdev->dev, "dma_trace_pages: %d\n", sdev->dma_trace_pages); + + ret = trace_debugfs_create(sdev); + if (ret < 0) + goto table_err; + + /* set IPC parameters */ + params.hdr.size = sizeof(params); + params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_PARAMS; + params.buffer.phy_addr = sdev->dmatp.addr; + params.buffer.size = sdev->dmatb.bytes; + params.buffer.offset = 0; + params.buffer.pages = sdev->dma_trace_pages; + + init_waitqueue_head(&sdev->trace_sleep); + sdev->host_offset = 0; + + ret = snd_sof_dma_trace_init(sdev, ¶ms.stream_tag); + if (ret < 0) { + dev_err(sdev->dev, + "error: fail in snd_sof_dma_trace_init %d\n", ret); + goto table_err; + } + dev_dbg(sdev->dev, "stream_tag: %d\n", params.stream_tag); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + params.hdr.cmd, ¶ms, sizeof(params), + &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) { + dev_err(sdev->dev, + "error: can't set params for DMA for trace %d\n", ret); + goto table_err; + } + + ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, + "error: snd_sof_dma_trace_trigger: start: %d\n", ret); + goto table_err; + } + + sdev->dtrace_is_enabled = true; + return 0; + +table_err: + snd_dma_free_pages(&sdev->dmatb); +page_err: + snd_dma_free_pages(&sdev->dmatp); + return ret; +} +EXPORT_SYMBOL(snd_sof_init_trace); + +int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn) +{ + if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) { + sdev->host_offset = posn->host_offset; + wake_up(&sdev->trace_sleep); + } + + if (posn->overflow != 0) + dev_err(sdev->dev, + "error: DSP trace buffer overflow %u bytes. Total messages %d\n", + posn->overflow, posn->messages); + + return 0; +} + +void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) +{ + if (sdev->dtrace_is_enabled) { + dev_err(sdev->dev, "error: waking up any trace sleepers\n"); + wake_up(&sdev->trace_sleep); + } +} +EXPORT_SYMBOL(snd_sof_trace_notify_for_error); + +void snd_sof_release_trace(struct snd_sof_dev *sdev) +{ + int ret; + + if (!sdev->dtrace_is_enabled) + return; + + ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) + dev_err(sdev->dev, + "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); + + ret = snd_sof_dma_trace_release(sdev); + if (ret < 0) + dev_err(sdev->dev, + "error: fail in snd_sof_dma_trace_release %d\n", ret); + + snd_dma_free_pages(&sdev->dmatb); + snd_dma_free_pages(&sdev->dmatp); +} +EXPORT_SYMBOL(snd_sof_release_trace); From 95b1b4eb36f764b864fcd122610d36c636008d1a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:33:43 +0000 Subject: [PATCH 113/285] ASoC: SOF: Add DSP HW abstraction operations Add operation pointers that can be called by core to control a wide variety of DSP targets. The DSP HW drivers will fill in these operations. Signed-off-by: Liam Girdwood --- sound/soc/sof/ops.c | 210 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/ops.h | 252 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 462 insertions(+) create mode 100644 sound/soc/sof/ops.c create mode 100644 sound/soc/sof/ops.h diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c new file mode 100644 index 00000000000000..a3b1aebb0fd14a --- /dev/null +++ b/sound/soc/sof/ops.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ops.h" +#include "sof-priv.h" + +int snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value) +{ + bool change; + unsigned int old, new; + u32 ret; + + pci_read_config_dword(sdev->pci, offset, &ret); + dev_dbg(sdev->dev, "Debug PCIR: %8.8x at %8.8x\n", + pci_read_config_dword(sdev->pci, offset, &ret), offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) { + pci_write_config_dword(sdev->pci, offset, new); + dev_dbg(sdev->dev, "Debug PCIW: %8.8x at %8.8x\n", value, + offset); + } + + return change; +} +EXPORT_SYMBOL(snd_sof_pci_update_bits_unlocked); + +int snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sdev->hw_lock, flags); + change = snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); + return change; +} +EXPORT_SYMBOL(snd_sof_pci_update_bits); + +int snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value) +{ + bool change; + unsigned int old, new; + u32 ret; + + ret = snd_sof_dsp_read(sdev, bar, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + snd_sof_dsp_write(sdev, bar, offset, new); + + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_unlocked); + +int snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 mask, u64 value) +{ + bool change; + u64 old, new; + + old = snd_sof_dsp_read64(sdev, bar, offset); + + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + snd_sof_dsp_write64(sdev, bar, offset, new); + + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value) +{ + unsigned int old, new; + u32 ret; + + ret = snd_sof_dsp_read(sdev, bar, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + snd_sof_dsp_write(sdev, bar, offset, new); +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced_unlocked); + +int snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sdev->hw_lock, flags); + change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, + value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits); + +int snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u64 mask, u64 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sdev->hw_lock, flags); + change = snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, + value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits64); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&sdev->hw_lock, flags); + snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value); + spin_unlock_irqrestore(&sdev->hw_lock, flags); +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); + +int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 target, u32 timeout) +{ + int time, ret; + bool done = false; + + /* + * we will poll for couple of ms using mdelay, if not successful + * then go to longer sleep using usleep_range + */ + + /* check if set state successful */ + for (time = 0; time < 5; time++) { + if ((snd_sof_dsp_read(sdev, bar, offset) & mask) == target) { + done = true; + break; + } + msleep(20); + } + + if (!done) { + /* sleeping in 10ms steps so adjust timeout value */ + timeout /= 10; + + for (time = 0; time < timeout; time++) { + if ((snd_sof_dsp_read(sdev, bar, offset) & mask) == + target) + break; + + usleep_range(5000, 10000); + } + } + + ret = time < timeout ? 0 : -ETIME; + + return ret; +} +EXPORT_SYMBOL(snd_sof_dsp_register_poll); + +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) +{ + dev_err(sdev->dev, "error : DSP panic!\n"); + + /* check if DSP is not ready and did not set the dsp_oops_offset. + * if the dsp_oops_offset is not set, set it from the panic message. + * Also add a check to memory window setting with panic message. + */ + if (!sdev->dsp_oops_offset) + sdev->dsp_oops_offset = offset; + else + dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n", + sdev->dsp_oops_offset, offset); + + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + snd_sof_trace_notify_for_error(sdev); + snd_sof_dsp_cmd_done(sdev); +} +EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h new file mode 100644 index 00000000000000..1a00a4b7d8037f --- /dev/null +++ b/sound/soc/sof/ops.h @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOUND_SOC_SOF_IO_H +#define __SOUND_SOC_SOF_IO_H + +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* init */ +static inline int snd_sof_probe(struct snd_sof_dev *sdev) +{ + if (sdev->ops->probe) + return sdev->ops->probe(sdev); + else + return 0; +} + +static inline int snd_sof_remove(struct snd_sof_dev *sdev) +{ + if (sdev->ops->remove) + return sdev->ops->remove(sdev); + else + return 0; +} + +/* control */ +static inline int snd_sof_dsp_run(struct snd_sof_dev *sdev) +{ + if (sdev->ops->run) + return sdev->ops->run(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_stall(struct snd_sof_dev *sdev) +{ + if (sdev->ops->stall) + return sdev->ops->stall(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_reset(struct snd_sof_dev *sdev) +{ + if (sdev->ops->reset) + return sdev->ops->reset(sdev); + else + return 0; +} + +/* power management */ +static inline int snd_sof_dsp_resume(struct snd_sof_dev *sdev) +{ + if (sdev->ops->resume) + return sdev->ops->resume(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_suspend(struct snd_sof_dev *sdev, int state) +{ + if (sdev->ops->suspend) + return sdev->ops->suspend(sdev, state); + else + return 0; +} + +static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq) +{ + if (sdev->ops->set_clk) + return sdev->ops->set_clk(sdev, freq); + else + return 0; +} + +/* debug */ +static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) +{ + if (sdev->ops->dbg_dump) + return sdev->ops->dbg_dump(sdev, flags); +} + +/* register IO */ +static inline void snd_sof_dsp_write(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 value) +{ + if (sdev->ops->write) + sdev->ops->write(sdev, sdev->bar[bar] + offset, value); +} + +static inline void snd_sof_dsp_write64(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 value) +{ + if (sdev->ops->write64) + sdev->ops->write64(sdev, + sdev->bar[bar] + offset, value); +} + +static inline u32 snd_sof_dsp_read(struct snd_sof_dev *sdev, u32 bar, + u32 offset) +{ + if (sdev->ops->read) + return sdev->ops->read(sdev, sdev->bar[bar] + offset); + else + return 0; +} + +static inline u64 snd_sof_dsp_read64(struct snd_sof_dev *sdev, u32 bar, + u32 offset) +{ + if (sdev->ops->read64) + return sdev->ops->read64(sdev, sdev->bar[bar] + offset); + else + return 0; +} + +/* block IO */ +static inline void snd_sof_dsp_block_read(struct snd_sof_dev *sdev, + u32 offset, void *dest, size_t bytes) +{ + if (sdev->ops->block_read) + sdev->ops->block_read(sdev, offset, dest, bytes); +} + +static inline void snd_sof_dsp_block_write(struct snd_sof_dev *sdev, + u32 offset, void *src, size_t bytes) +{ + if (sdev->ops->block_write) + sdev->ops->block_write(sdev, offset, src, bytes); +} + +/* mailbox */ +static inline void snd_sof_dsp_mailbox_read(struct snd_sof_dev *sdev, + u32 offset, void *message, + size_t bytes) +{ + if (sdev->ops->mailbox_read) + sdev->ops->mailbox_read(sdev, offset, message, bytes); +} + +static inline void snd_sof_dsp_mailbox_write(struct snd_sof_dev *sdev, + u32 offset, void *message, + size_t bytes) +{ + if (sdev->ops->mailbox_write) + sdev->ops->mailbox_write(sdev, offset, message, bytes); +} + +/* ipc */ +static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + if (sdev->ops->send_msg) + return sdev->ops->send_msg(sdev, msg); + else + return 0; +} + +static inline int snd_sof_dsp_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + if (sdev->ops->get_reply) + return sdev->ops->get_reply(sdev, msg); + else + return 0; +} + +static inline int snd_sof_dsp_is_ready(struct snd_sof_dev *sdev) +{ + if (sdev->ops->is_ready) + return sdev->ops->is_ready(sdev); + else + return 0; +} + +static inline int snd_sof_dsp_cmd_done(struct snd_sof_dev *sdev) +{ + if (sdev->ops->cmd_done) + return sdev->ops->cmd_done(sdev); + else + return 0; +} + +/* host DMA trace */ +static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev, + u32 *stream_tag) +{ + if (sdev->ops->trace_init) + return sdev->ops->trace_init(sdev, stream_tag); + else + return 0; +} + +static inline int snd_sof_dma_trace_release(struct snd_sof_dev *sdev) +{ + if (sdev->ops->trace_release) + return sdev->ops->trace_release(sdev); + else + return 0; +} + +static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd) +{ + if (sdev->ops->trace_trigger) + return sdev->ops->trace_trigger(sdev, cmd); + else + return 0; +} + +int snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); + +int snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 mask, u64 value); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); + +int snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 value); + +int snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u64 mask, u64 value); + +/* This is for registers bits with attribute RWC */ +void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); + +int snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value); + +int snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, + u32 mask, u32 value); + +int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 target, u32 timeout); + +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset); +#endif From 41ff0a488b22099476b53e9096d5bf66aa1e5e83 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:34:01 +0000 Subject: [PATCH 114/285] ASoC: SOF: Add firmware loader support The firmware loader exports APIs that can be called by core to load and process multiple different file formats. Signed-off-by: Liam Girdwood --- sound/soc/sof/loader.c | 283 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 sound/soc/sof/loader.c diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c new file mode 100644 index 00000000000000..82d6c6ccc65fb2 --- /dev/null +++ b/sound/soc/sof/loader.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +static int get_ext_windows(struct snd_sof_dev *sdev, + struct sof_ipc_ext_data_hdr *ext_hdr) +{ + struct sof_ipc_window *w = (struct sof_ipc_window *)ext_hdr; + + int ret = 0; + size_t size; + + if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) + return -EINVAL; + + size = sizeof(*w) + sizeof(struct sof_ipc_window_elem) * w->num_windows; + + /* keep a local copy of the data */ + sdev->info_window = kmemdup(w, size, GFP_KERNEL); + if (!sdev->info_window) + return -ENOMEM; + + return ret; +} + +int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) +{ + struct sof_ipc_ext_data_hdr *ext_hdr; + void *ext_data; + int ret = 0; + + ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!ext_data) + return -ENOMEM; + + /* get first header */ + snd_sof_dsp_block_read(sdev, offset, ext_data, sizeof(*ext_hdr)); + ext_hdr = (struct sof_ipc_ext_data_hdr *)ext_data; + + while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { + /* read in ext structure */ + offset += sizeof(*ext_hdr); + snd_sof_dsp_block_read(sdev, offset, + ext_data + sizeof(*ext_hdr), + ext_hdr->hdr.size - sizeof(*ext_hdr)); + + dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", + ext_hdr->type, ext_hdr->hdr.size); + + /* process structure data */ + switch (ext_hdr->type) { + case SOF_IPC_EXT_DMA_BUFFER: + break; + case SOF_IPC_EXT_WINDOW: + ret = get_ext_windows(sdev, ext_hdr); + break; + default: + break; + } + + if (ret < 0) { + dev_err(sdev->dev, "error: failed to parse ext data type %d\n", + ext_hdr->type); + } + + /* move to next header */ + offset += ext_hdr->hdr.size; + snd_sof_dsp_block_read(sdev, offset, ext_data, + sizeof(*ext_hdr)); + ext_hdr = (struct sof_ipc_ext_data_hdr *)ext_data; + } + + kfree(ext_data); + return ret; +} +EXPORT_SYMBOL(snd_sof_fw_parse_ext_data); + +/* generic module parser for mmaped DSPs */ +int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, + struct snd_sof_mod_hdr *module) +{ + struct snd_sof_blk_hdr *block; + int count; + u32 offset; + + dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n", + module->size, module->num_blocks, module->type); + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->num_blocks; count++) { + if (block->size == 0) { + dev_warn(sdev->dev, + "warning: block %d size zero\n", count); + dev_warn(sdev->dev, " type 0x%x offset 0x%x\n", + block->type, block->offset); + continue; + } + + switch (block->type) { + case SOF_BLK_IMAGE: + case SOF_BLK_CACHE: + case SOF_BLK_REGS: + case SOF_BLK_SIG: + case SOF_BLK_ROM: + continue; /* not handled atm */ + case SOF_BLK_TEXT: + case SOF_BLK_DATA: + offset = block->offset; + break; + default: + dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", + block->type, count); + return -EINVAL; + } + + dev_dbg(sdev->dev, + "block %d type 0x%x size 0x%x ==> offset 0x%x\n", + count, block->type, block->size, offset); + + snd_sof_dsp_block_write(sdev, offset, + (void *)block + sizeof(*block), + block->size); + + /* next block */ + block = (void *)block + sizeof(*block) + block->size; + } + + return 0; +} +EXPORT_SYMBOL(snd_sof_parse_module_memcpy); + +static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw) +{ + struct snd_sof_fw_header *header; + + /* Read the header information from the data pointer */ + header = (struct snd_sof_fw_header *)fw->data; + + /* verify FW sig */ + if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { + dev_err(sdev->dev, "error: invalid firmware signature\n"); + return -EINVAL; + } + + /* check size is valid */ + if (fw->size != header->file_size + sizeof(*header)) { + dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n", + fw->size, header->file_size + sizeof(*header)); + return -EINVAL; + } + + dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", + header->file_size, header->num_modules, + header->abi, sizeof(*header)); + + return 0; +} + +static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw) +{ + struct snd_sof_fw_header *header; + struct snd_sof_mod_hdr *module; + int (*load_module)(struct snd_sof_dev *sof_dev, + struct snd_sof_mod_hdr *hdr); + int ret, count; + + header = (struct snd_sof_fw_header *)fw->data; + load_module = sdev->ops->load_module; + if (!load_module) + return -EINVAL; + + /* parse each module */ + module = (void *)fw->data + sizeof(*header); + for (count = 0; count < header->num_modules; count++) { + /* module */ + ret = load_module(sdev, module); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid module %d\n", count); + return ret; + } + module = (void *)module + sizeof(*module) + module->size; + } + + return 0; +} + +int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, + const struct firmware *fw) +{ + int ret; + + /* make sure the FW header and file is valid */ + ret = check_header(sdev, fw); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid FW header\n"); + return ret; + } + + /* prepare the DSP for FW loading */ + ret = snd_sof_dsp_reset(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to reset DSP\n"); + return ret; + } + + /* parse and load firmware modules to DSP */ + ret = load_modules(sdev, fw); + if (ret < 0) { + dev_err(sdev->dev, "error: invalid FW modules\n"); + return ret; + } + + return ret; +} +EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); + +int snd_sof_load_firmware(struct snd_sof_dev *sdev, + const struct firmware *fw) +{ + dev_dbg(sdev->dev, "loading firmware\n"); + + if (sdev->ops->load_firmware) + return sdev->ops->load_firmware(sdev, fw); + return 0; +} +EXPORT_SYMBOL(snd_sof_load_firmware); + +int snd_sof_run_firmware(struct snd_sof_dev *sdev) +{ + int ret; + + init_waitqueue_head(&sdev->boot_wait); + sdev->boot_complete = false; + + dev_dbg(sdev->dev, "booting DSP firmware\n"); + + /* boot the firmware on the DSP */ + ret = snd_sof_dsp_run(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to reset DSP\n"); + return ret; + } + + /* now wait for the DSP to boot */ + ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete, + msecs_to_jiffies(sdev->boot_timeout)); + if (ret == 0) { + dev_err(sdev->dev, "error: firmware boot timeout\n"); + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX | + SOF_DBG_TEXT | SOF_DBG_PCI); + return -EIO; + } + + dev_info(sdev->dev, "firmware boot complete\n"); + + return 0; +} +EXPORT_SYMBOL(snd_sof_run_firmware); + +void snd_sof_fw_unload(struct snd_sof_dev *sdev) +{ +} +EXPORT_SYMBOL(snd_sof_fw_unload); From a8711d68ea39f819dabb44cb8fd13d3d4564b138 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:34:16 +0000 Subject: [PATCH 115/285] ASoC: SOF: Add compressed PCM support Add support for compressed audio playback/capture in SOF. TODO: to be completed. Signed-off-by: Liam Girdwood --- sound/soc/sof/compressed.c | 168 +++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 sound/soc/sof/compressed.c diff --git a/sound/soc/sof/compressed.c b/sound/soc/sof/compressed.c new file mode 100644 index 00000000000000..64b35fb0e6e5ed --- /dev/null +++ b/sound/soc/sof/compressed.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +static int sof_compressed_open(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + + mutex_lock(&spcm->mutex); + pm_runtime_get_sync(sdev->dev); + mutex_unlock(&spcm->mutex); + return 0; +} + +static int sof_compressed_free(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + + mutex_lock(&spcm->mutex); + pm_runtime_put(sdev->dev); + mutex_unlock(&spcm->mutex); + return 0; +} + +static int sof_vorbis_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + return 0; +} + +static int sof_mp3_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + return 0; +} + +static int sof_compressed_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + + switch (params->codec.id) { + case SND_AUDIOCODEC_VORBIS: + return sof_vorbis_set_params(cstream, params); + case SND_AUDIOCODEC_MP3: + return sof_mp3_set_params(cstream, params); + default: + dev_err(sdev->dev, "error: codec id %d not supported\n", + params->codec.id); + return -EINVAL; + } +} + +static int sof_compressed_trigger(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct snd_sof_pcm *spcm = rtd->sof; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; + stream.comp_id = spcm->stream[cstream->direction].comp_id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + break; + case SNDRV_PCM_TRIGGER_STOP: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_RESUME: + default: + break; + } + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); +} + +static int sof_compressed_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_sof_dev *sdev = + snd_soc_platform_get_drvdata(rtd->platform); + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm = rtd->sof; + + snd_sof_ipc_stream_posn(sdev, spcm, cstream->direction, &posn); + + dev_vdbg(sdev->dev, "CPCM: DMA position %llu DAI position %llu\n", + posn.host_posn, posn.dai_posn); + + return 0; +} + +static int sof_compressed_ack(struct snd_compr_stream *cstream, + size_t bytes) +{ + return 0; +} + +static int sof_compressed_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *caps) +{ + return 0; +} + +static int sof_compressed_get_codec_caps(struct snd_compr_stream *cstream, + struct snd_compr_codec_caps *codec) +{ + return 0; +} + +static int sof_compressed_set_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + return 0; +} + +struct snd_compr_ops sof_compressed_ops = { + .open = sof_compressed_open, + .free = sof_compressed_free, + .set_params = sof_compressed_set_params, + .set_metadata = sof_compressed_set_metadata, + .trigger = sof_compressed_trigger, + .pointer = sof_compressed_pointer, + .ack = sof_compressed_ack, + .get_caps = sof_compressed_get_caps, + .get_codec_caps = sof_compressed_get_codec_caps, +}; From 7c7884f7270f872af709eb49f76a431a13fe5cda Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 16 Jan 2018 15:36:57 +0000 Subject: [PATCH 116/285] ASoC: SOF: Add PM support Add support for saving and restoring DSP context in D3 to host DDR. Signed-off-by: Liam Girdwood --- sound/soc/sof/pm.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 sound/soc/sof/pm.c diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c new file mode 100644 index 00000000000000..d825ff402fc66a --- /dev/null +++ b/sound/soc/sof/pm.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +int snd_sof_runtime_suspend(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_runtime_suspend); + +int snd_sof_runtime_resume(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_runtime_resume); + +int snd_sof_resume(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_resume); + +int snd_sof_suspend(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_suspend); + +int snd_sof_suspend_late(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(snd_sof_suspend_late); From 543caaf6ef64b52cdce9bc91a05d30eff7de723a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:37:08 +0000 Subject: [PATCH 117/285] ASoC: SOF: Add Nocodec machine driver support Add a simple "fallback" machine driver that can be used to enable SOF on boards with no codec device. This machine driver can also be forced for debug/development. Signed-off-by: Liam Girdwood --- include/sound/sof.h | 5 ++ sound/soc/sof/nocodec.c | 114 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 sound/soc/sof/nocodec.c diff --git a/include/sound/sof.h b/include/sound/sof.h index 7767ec8ce188d6..f6056247c3a75f 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -73,4 +73,9 @@ struct sof_dev_desc { const char *nocodec_tplg_filename; }; +int sof_nocodec_setup(struct device *dev, + struct snd_sof_pdata *sof_pdata, + struct snd_soc_acpi_mach *mach, + const struct sof_dev_desc *desc); + #endif diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c new file mode 100644 index 00000000000000..0685d29f9d28b1 --- /dev/null +++ b/sound/soc/sof/nocodec.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int sof_nocodec_setup(struct device *dev, + struct snd_sof_pdata *sof_pdata, + struct snd_soc_acpi_mach *mach, + const struct sof_dev_desc *desc) +{ + if (!mach) + return -EINVAL; + + sof_pdata->drv_name = "sof-nocodec"; + + mach->drv_name = "sof-nocodec"; + mach->sof_fw_filename = desc->nocodec_fw_filename; + mach->sof_tplg_filename = desc->nocodec_tplg_filename; + + return 0; +} +EXPORT_SYMBOL(sof_nocodec_setup); + +static int sof_nocodec_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + // TODO: read this from topology + return 0; +} + +static struct snd_soc_ops sof_nocodec_ops = {}; + +static int nocodec_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + snd_soc_set_dmi_name(rtd->card, NULL); + + return 0; +} + +/* we just set some BEs - FE provided by topology */ +static struct snd_soc_dai_link sof_nocodec_dais[] = { + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "NoCodec", + .id = 0, + .init = nocodec_rtd_init, + .cpu_dai_name = "sof-audio", + .platform_name = "sof-audio", + .no_pcm = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ops = &sof_nocodec_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .be_hw_params_fixup = sof_nocodec_codec_fixup, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card sof_nocodec_card = { + .name = "sof-nocodec", + .dai_link = sof_nocodec_dais, + .num_links = ARRAY_SIZE(sof_nocodec_dais), +}; + +static int sof_nocodec_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &sof_nocodec_card; + + card->dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static int sof_nocodec_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver sof_nocodec_audio = { + .probe = sof_nocodec_probe, + .remove = sof_nocodec_remove, + .driver = { + .name = "sof-nocodec", + .pm = &snd_soc_pm_ops, + }, +}; +module_platform_driver(sof_nocodec_audio) + +MODULE_DESCRIPTION("ASoC sof nocodec"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:sof-nocodec"); From 8c5bb0fade1c8e6605bec547d67ea28cc3e18071 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:37:40 +0000 Subject: [PATCH 118/285] ASoC: SOF: Intel: Add BYT, CHT and BSW DSP HW support. Add support for the audio DSP hardware found on Intel Baytrail, Cherrytrail and Braswell based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/byt.c | 846 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/shim.h | 157 +++++++ 2 files changed, 1003 insertions(+) create mode 100644 sound/soc/sof/intel/byt.c create mode 100644 sound/soc/sof/intel/shim.h diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c new file mode 100644 index 00000000000000..3103987207e4ae --- /dev/null +++ b/sound/soc/sof/intel/byt.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardware interface for audio DSP on Baytrail, Braswell and Cherrytrail. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "shim.h" + +/* DSP memories */ +#define IRAM_OFFSET 0x0C0000 +#define IRAM_SIZE (80 * 1024) +#define DRAM_OFFSET 0x100000 +#define DRAM_SIZE (160 * 1024) +#define SHIM_OFFSET 0x140000 +#define SHIM_SIZE 0x100 +#define MBOX_OFFSET 0x144000 +#define MBOX_SIZE 0x1000 +#define EXCEPT_OFFSET 0x800 + +/* DSP peripherals */ +#define DMAC0_OFFSET 0x098000 +#define DMAC1_OFFSET 0x09c000 +#define DMAC2_OFFSET 0x094000 +#define DMAC_SIZE 0x420 +#define SSP0_OFFSET 0x0a0000 +#define SSP1_OFFSET 0x0a1000 +#define SSP2_OFFSET 0x0a2000 +#define SSP3_OFFSET 0x0a4000 +#define SSP4_OFFSET 0x0a5000 +#define SSP5_OFFSET 0x0a6000 +#define SSP_SIZE 0x100 + +#define BYT_STACK_DUMP_SIZE 32 + +#define BYT_PCI_BAR_SIZE 0x200000 + +#define BYT_PANIC_OFFSET(x) (((x) & (0xFFFFll << 32)) >> 32) + +/* + * Debug + */ + +#define MBOX_DUMP_SIZE 0x30 + +/* BARs */ +#define BYT_DSP_BAR 0 +#define BYT_PCI_BAR 1 +#define BYT_IMR_BAR 2 + +static const struct snd_sof_debugfs_map byt_debugfs[] = { + {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE}, + {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE}, + {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE}, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +static const struct snd_sof_debugfs_map cht_debugfs[] = { + {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"dmac2", BYT_DSP_BAR, DMAC2_OFFSET, DMAC_SIZE}, + {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE}, + {"ssp3", BYT_DSP_BAR, SSP3_OFFSET, SSP_SIZE}, + {"ssp4", BYT_DSP_BAR, SSP4_OFFSET, SSP_SIZE}, + {"ssp5", BYT_DSP_BAR, SSP5_OFFSET, SSP_SIZE}, + {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE}, + {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE}, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +/* + * Register IO + */ + +static void byt_write(struct snd_sof_dev *sdev, void __iomem *addr, + u32 value) +{ + writel(value, addr); +} + +static u32 byt_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +static void byt_write64(struct snd_sof_dev *sdev, void __iomem *addr, + u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +static u64 byt_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * Memory copy. + */ + +static void byt_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy((void *)dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + } +} + +static void byt_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +/* + * IPC Firmware ready. + */ +static void byt_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = elem->offset + MBOX_OFFSET; + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = elem->offset + MBOX_OFFSET; + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = elem->offset + MBOX_OFFSET; + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + stream_offset, + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + MBOX_OFFSET; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BYT_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +static int byt_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = MBOX_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + byt_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + + snd_sof_dsp_mailbox_init(sdev, fw_ready->dspbox_offset, + fw_ready->dspbox_size, + fw_ready->hostbox_offset, + fw_ready->hostbox_size); + + dev_info(sdev->dev, + " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, MBOX_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + byt_get_windows(sdev); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static void byt_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +static void byt_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Debug + */ + +static void byt_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read regsisters */ + byt_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* the get the stack */ + byt_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +static void byt_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[BYT_STACK_DUMP_SIZE]; + u32 status, panic; + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCX); + byt_get_registers(sdev, &xoops, stack, BYT_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + BYT_STACK_DUMP_SIZE); +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t byt_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u64 isr; + int ret = IRQ_NONE; + + /* Interrupt arrived, check src */ + isr = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_ISRX); + if (isr & SHIM_ISRX_DONE) { + /* Mask Done interrupt before return */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, + SHIM_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SHIM_ISRX_BUSY) { + /* Mask Busy interrupt before return */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, + SHIM_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +static irqreturn_t byt_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u64 ipcx, ipcd; + + ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX); + + /* reply message from DSP */ + if (ipcx & SHIM_BYT_IPCX_DONE) { + /* Handle Immediate reply from DSP Core */ + snd_sof_ipc_reply(sdev, ipcx); + + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCX, + SHIM_BYT_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } + + /* new message from DSP */ + ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD); + if (ipcd & SHIM_BYT_IPCD_BUSY) { + /* Handle messages from DSP Core */ + if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, BYT_PANIC_OFFSET(ipcd) + + MBOX_OFFSET); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + } + + return IRQ_HANDLED; +} + +static int byt_is_ready(struct snd_sof_dev *sdev) +{ + u64 imrx; + + imrx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRX); + if (imrx & SHIM_IMRX_DONE) + return 0; + + return 1; +} + +static int byt_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + u64 cmd = msg->header; + + /* send the message */ + byt_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write64(sdev, BYT_DSP_BAR, SHIM_IPCX, + cmd | SHIM_BYT_IPCX_BUSY); + + return 0; +} + +static int byt_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + byt_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + byt_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + + return ret; +} + +static int byt_cmd_done(struct snd_sof_dev *sdev) +{ + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCD, + SHIM_BYT_IPCD_BUSY | + SHIM_BYT_IPCD_DONE, + SHIM_BYT_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + + return 0; +} + +/* + * DSP control. + */ + +static int byt_run(struct snd_sof_dev *sdev) +{ + int tries = 10; + + /* release stall and wait to unstall */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR, + SHIM_BYT_CSR_STALL, 0x0); + while (tries--) { + if (!(snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_CSR) & + SHIM_BYT_CSR_PWAITMODE)) + break; + msleep(100); + } + if (tries < 0) { + dev_err(sdev->dev, "error: unable to run DSP firmware\n"); + byt_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + return -ENODEV; + } + + return 0; +} + +static int byt_reset(struct snd_sof_dev *sdev) +{ + /* put DSP into reset, set reset vector and stall */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR, + SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL | + SHIM_BYT_CSR_STALL, + SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL | + SHIM_BYT_CSR_STALL); + + usleep_range(10, 15); + + /* take DSP out of reset and keep stalled for FW loading */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR, + SHIM_BYT_CSR_RST, 0); + + return 0; +} + +/* + * Probe and remove. + */ + +static int byt_acpi_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct resource *mmio; + u32 base, size; + int ret = 0; + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* LPE base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get LPE base at idx %d\n", + desc->resindex_lpe_base); + return -EINVAL; + } + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_DSP_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]); + + /* TODO: add offsets */ + sdev->mmio_bar = BYT_DSP_BAR; + sdev->mailbox_bar = BYT_DSP_BAR; + + /* IMR base - optional */ + if (desc->resindex_imr_base == -1) + goto irq; + + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_imr_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get IMR base at idx %d\n", + desc->resindex_imr_base); + ret = -ENODEV; + goto imr_err; + } + + /* some BIOSes don't map IMR */ + if (base == 0x55aa55aa || base == 0x0) { + dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n"); + goto irq; + } + + dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_IMR_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_IMR_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto imr_err; + } + dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]); + +irq: + /* register our IRQ */ + sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + if (sdev->ipc_irq < 0) { + dev_err(sdev->dev, "error: failed to get IRQ at index %d\n", + desc->irqindex_host_ipc); + ret = sdev->ipc_irq; + goto irq_err; + } + + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, byt_irq_handler, + byt_irq_thread, IRQF_SHARED, "AudioDSP", + sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable Interrupt from both sides */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x0); + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x0); + + /* set BARS */ + sdev->cl_bar = BYT_DSP_BAR; + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = MBOX_OFFSET; + + return ret; + +irq_err: + iounmap(sdev->bar[BYT_IMR_BAR]); +imr_err: + iounmap(sdev->bar[BYT_DSP_BAR]); + return ret; +} + +static int byt_pci_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct pci_dev *pci = sdev->pci; + u32 base, size; + int ret = 0; + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* LPE base */ + base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET; + size = BYT_PCI_BAR_SIZE; + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_DSP_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]); + + /* IMR base - optional */ + if (desc->resindex_imr_base == -1) + goto irq; + + base = pci_resource_start(pci, desc->resindex_imr_base); + size = pci_resource_len(pci, desc->resindex_imr_base); + + /* some BIOSes don't map IMR */ + if (base == 0x55aa55aa || base == 0x0) { + dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n"); + goto irq; + } + + dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size); + sdev->bar[BYT_IMR_BAR] = ioremap(base, size); + if (!sdev->bar[BYT_IMR_BAR]) { + dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto imr_err; + } + dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]); + +irq: + /* register our IRQ */ + sdev->ipc_irq = pci->irq; + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, byt_irq_handler, + byt_irq_thread, 0, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable Interrupt from both sides */ + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x0); + snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x0); + + /* set BARS */ + sdev->cl_bar = BYT_DSP_BAR; + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = MBOX_OFFSET; + + return ret; + +irq_err: + iounmap(sdev->bar[BYT_IMR_BAR]); +imr_err: + iounmap(sdev->bar[BYT_DSP_BAR]); + return ret; +} + +static int byt_probe(struct snd_sof_dev *sdev) +{ + if (sdev->pci) + return byt_pci_probe(sdev); + else + return byt_acpi_probe(sdev); +} + +static int byt_acpi_remove(struct snd_sof_dev *sdev) +{ + iounmap(sdev->bar[BYT_DSP_BAR]); + iounmap(sdev->bar[BYT_PCI_BAR]); + iounmap(sdev->bar[BYT_IMR_BAR]); + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +static int byt_pci_remove(struct snd_sof_dev *sdev) +{ + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +static int byt_remove(struct snd_sof_dev *sdev) +{ + if (sdev->pci) + return byt_pci_remove(sdev); + else + return byt_acpi_remove(sdev); +} + +/* baytrail ops */ +struct snd_sof_dsp_ops sof_byt_ops = { + /* device init */ + .probe = byt_probe, + .remove = byt_remove, + + /* DSP core boot / reset */ + .run = byt_run, + .reset = byt_reset, + + /* Register IO */ + .write = byt_write, + .read = byt_read, + .write64 = byt_write64, + .read64 = byt_read64, + + /* Block IO */ + .block_read = byt_block_read, + .block_write = byt_block_write, + + /* doorbell */ + .irq_handler = byt_irq_handler, + .irq_thread = byt_irq_thread, + + /* mailbox */ + .mailbox_read = byt_mailbox_read, + .mailbox_write = byt_mailbox_write, + + /* ipc */ + .send_msg = byt_send_msg, + .get_reply = byt_get_reply, + .fw_ready = byt_fw_ready, + .is_ready = byt_is_ready, + .cmd_done = byt_cmd_done, + + /* debug */ + .debug_map = byt_debugfs, + .debug_map_count = ARRAY_SIZE(byt_debugfs), + .dbg_dump = byt_dump, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(sof_byt_ops); + +/* cherrytrail and braswell ops */ +struct snd_sof_dsp_ops sof_cht_ops = { + /* device init */ + .probe = byt_probe, + .remove = byt_remove, + + /* DSP core boot / reset */ + .run = byt_run, + .reset = byt_reset, + + /* Register IO */ + .write = byt_write, + .read = byt_read, + .write64 = byt_write64, + .read64 = byt_read64, + + /* Block IO */ + .block_read = byt_block_read, + .block_write = byt_block_write, + + /* doorbell */ + .irq_handler = byt_irq_handler, + .irq_thread = byt_irq_thread, + + /* mailbox */ + .mailbox_read = byt_mailbox_read, + .mailbox_write = byt_mailbox_write, + + /* ipc */ + .send_msg = byt_send_msg, + .get_reply = byt_get_reply, + .fw_ready = byt_fw_ready, + .is_ready = byt_is_ready, + .cmd_done = byt_cmd_done, + + /* debug */ + .debug_map = cht_debugfs, + .debug_map_count = ARRAY_SIZE(cht_debugfs), + .dbg_dump = byt_dump, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(sof_cht_ops); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h new file mode 100644 index 00000000000000..512441387b1e32 --- /dev/null +++ b/sound/soc/sof/intel/shim.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOF_INTEL_SHIM_H +#define __SOF_INTEL_SHIM_H + +/* + * SHIM registers for BYT, BSW, CHT, HSW, BDW + */ + +#define SHIM_CSR (SHIM_OFFSET + 0x00) +#define SHIM_PISR (SHIM_OFFSET + 0x08) +#define SHIM_PIMR (SHIM_OFFSET + 0x10) +#define SHIM_ISRX (SHIM_OFFSET + 0x18) +#define SHIM_ISRD (SHIM_OFFSET + 0x20) +#define SHIM_IMRX (SHIM_OFFSET + 0x28) +#define SHIM_IMRD (SHIM_OFFSET + 0x30) +#define SHIM_IPCX (SHIM_OFFSET + 0x38) +#define SHIM_IPCD (SHIM_OFFSET + 0x40) +#define SHIM_ISRSC (SHIM_OFFSET + 0x48) +#define SHIM_ISRLPESC (SHIM_OFFSET + 0x50) +#define SHIM_IMRSC (SHIM_OFFSET + 0x58) +#define SHIM_IMRLPESC (SHIM_OFFSET + 0x60) +#define SHIM_IPCSC (SHIM_OFFSET + 0x68) +#define SHIM_IPCLPESC (SHIM_OFFSET + 0x70) +#define SHIM_CLKCTL (SHIM_OFFSET + 0x78) +#define SHIM_CSR2 (SHIM_OFFSET + 0x80) +#define SHIM_LTRC (SHIM_OFFSET + 0xE0) +#define SHIM_HMDC (SHIM_OFFSET + 0xE8) + +#define SHIM_PWMCTRL 0x1000 + +/* + * SST SHIM register bits for BYT, BSW, CHT HSW, BDW + * Register bit naming and functionaility can differ between devices. + */ + +/* CSR / CS */ +#define SHIM_CSR_RST (0x1 << 1) +#define SHIM_CSR_SBCS0 (0x1 << 2) +#define SHIM_CSR_SBCS1 (0x1 << 3) +#define SHIM_CSR_DCS(x) (x << 4) +#define SHIM_CSR_DCS_MASK (0x7 << 4) +#define SHIM_CSR_STALL (0x1 << 10) +#define SHIM_CSR_S0IOCS (0x1 << 21) +#define SHIM_CSR_S1IOCS (0x1 << 23) +#define SHIM_CSR_LPCS (0x1 << 31) +#define SHIM_CSR_24MHZ_LPCS \ + (SHIM_CSR_SBCS0 | SHIM_CSR_SBCS1 | SHIM_CSR_LPCS) +#define SHIM_CSR_24MHZ_NO_LPCS (SHIM_CSR_SBCS0 | SHIM_CSR_SBCS1) +#define SHIM_BYT_CSR_RST (0x1 << 0) +#define SHIM_BYT_CSR_VECTOR_SEL (0x1 << 1) +#define SHIM_BYT_CSR_STALL (0x1 << 2) +#define SHIM_BYT_CSR_PWAITMODE (0x1 << 3) + +/* ISRX / ISC */ +#define SHIM_ISRX_BUSY (0x1 << 1) +#define SHIM_ISRX_DONE (0x1 << 0) +#define SHIM_BYT_ISRX_REQUEST (0x1 << 1) + +/* ISRD / ISD */ +#define SHIM_ISRD_BUSY (0x1 << 1) +#define SHIM_ISRD_DONE (0x1 << 0) + +/* IMRX / IMC */ +#define SHIM_IMRX_BUSY (0x1 << 1) +#define SHIM_IMRX_DONE (0x1 << 0) +#define SHIM_BYT_IMRX_REQUEST (0x1 << 1) + +/* IMRD / IMD */ +#define SHIM_IMRD_DONE (0x1 << 0) +#define SHIM_IMRD_BUSY (0x1 << 1) +#define SHIM_IMRD_SSP0 (0x1 << 16) +#define SHIM_IMRD_DMAC0 (0x1 << 21) +#define SHIM_IMRD_DMAC1 (0x1 << 22) +#define SHIM_IMRD_DMAC (SHIM_IMRD_DMAC0 | SHIM_IMRD_DMAC1) + +/* IPCX / IPCC */ +#define SHIM_IPCX_DONE (0x1 << 30) +#define SHIM_IPCX_BUSY (0x1 << 31) +#define SHIM_BYT_IPCX_DONE ((u64)0x1 << 62) +#define SHIM_BYT_IPCX_BUSY ((u64)0x1 << 63) + +/* IPCD */ +#define SHIM_IPCD_DONE (0x1 << 30) +#define SHIM_IPCD_BUSY (0x1 << 31) +#define SHIM_BYT_IPCD_DONE ((u64)0x1 << 62) +#define SHIM_BYT_IPCD_BUSY ((u64)0x1 << 63) + +/* CLKCTL */ +#define SHIM_CLKCTL_SMOS(x) (x << 24) +#define SHIM_CLKCTL_MASK (3 << 24) +#define SHIM_CLKCTL_DCPLCG BIT(18) +#define SHIM_CLKCTL_SCOE1 BIT(17) +#define SHIM_CLKCTL_SCOE0 BIT(16) + +/* CSR2 / CS2 */ +#define SHIM_CSR2_SDFD_SSP0 BIT(1) +#define SHIM_CSR2_SDFD_SSP1 BIT(2) + +/* LTRC */ +#define SHIM_LTRC_VAL(x) (x << 0) + +/* HMDC */ +#define SHIM_HMDC_HDDA0(x) (x << 0) +#define SHIM_HMDC_HDDA1(x) (x << 7) +#define SHIM_HMDC_HDDA_E0_CH0 1 +#define SHIM_HMDC_HDDA_E0_CH1 2 +#define SHIM_HMDC_HDDA_E0_CH2 4 +#define SHIM_HMDC_HDDA_E0_CH3 8 +#define SHIM_HMDC_HDDA_E1_CH0 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH0) +#define SHIM_HMDC_HDDA_E1_CH1 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH1) +#define SHIM_HMDC_HDDA_E1_CH2 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH2) +#define SHIM_HMDC_HDDA_E1_CH3 SHIM_HMDC_HDDA1(SHIM_HMDC_HDDA_E0_CH3) +#define SHIM_HMDC_HDDA_E0_ALLCH \ + (SHIM_HMDC_HDDA_E0_CH0 | SHIM_HMDC_HDDA_E0_CH1 | \ + SHIM_HMDC_HDDA_E0_CH2 | SHIM_HMDC_HDDA_E0_CH3) +#define SHIM_HMDC_HDDA_E1_ALLCH \ + (SHIM_HMDC_HDDA_E1_CH0 | SHIM_HMDC_HDDA_E1_CH1 | \ + SHIM_HMDC_HDDA_E1_CH2 | SHIM_HMDC_HDDA_E1_CH3) + +/* Audio DSP PCI registers */ +#define PCI_VDRTCTL0 0xa0 +#define PCI_VDRTCTL1 0xa4 +#define PCI_VDRTCTL2 0xa8 +#define PCI_VDRTCTL3 0xaC + +/* VDRTCTL0 */ +#define PCI_VDRTCL0_D3PGD BIT(0) +#define PCI_VDRTCL0_D3SRAMPGD BIT(1) +#define PCI_VDRTCL0_DSRAMPGE_SHIFT 12 +#define PCI_VDRTCL0_DSRAMPGE_MASK (0xfffff << PCI_VDRTCL0_DSRAMPGE_SHIFT) +#define PCI_VDRTCL0_ISRAMPGE_SHIFT 2 +#define PCI_VDRTCL0_ISRAMPGE_MASK (0x3ff << PCI_VDRTCL0_ISRAMPGE_SHIFT) + +/* VDRTCTL2 */ +#define PCI_VDRTCL2_DCLCGE BIT(1) +#define PCI_VDRTCL2_DTCGE BIT(10) +#define PCI_VDRTCL2_APLLSE_MASK BIT(31) + +/* PMCS */ +#define PCI_PMCS 0x84 +#define PCI_PMCS_PS_MASK 0x3 + +extern struct snd_sof_dsp_ops sof_byt_ops; +extern struct snd_sof_dsp_ops sof_cht_ops; +extern struct snd_sof_dsp_ops sof_hsw_ops; +extern struct snd_sof_dsp_ops sof_bdw_ops; + +#endif From a289d928a2f5e3e3ef54eab45ba70b912c01b5f6 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:37:51 +0000 Subject: [PATCH 119/285] ASoC: SOF: Intel: Add HSW HW DSP support Add DSP hardware support for Intel Haswell based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hsw.c | 754 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 754 insertions(+) create mode 100644 sound/soc/sof/intel/hsw.c diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c new file mode 100644 index 00000000000000..b9a51021550790 --- /dev/null +++ b/sound/soc/sof/intel/hsw.c @@ -0,0 +1,754 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardwre interface for audio DSP on Haswell + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "shim.h" + +/* BARs */ +#define HSW_DSP_BAR 0 +#define HSW_PCI_BAR 1 + +/* + * Debug + */ + +/* DSP memories for HSW */ +#define IRAM_OFFSET 0x80000 +#define HSW_IRAM_SIZE (10 * 32 * 1024) +#define DRAM_OFFSET 0x00000 +#define HSW_DRAM_SIZE (16 * 32 * 1024) +#define SHIM_OFFSET 0xE7000 +#define SHIM_SIZE 0x100 +#define MBOX_OFFSET 0x7E000 +#define MBOX_SIZE 0x1000 +#define MBOX_DUMP_SIZE 0x30 +#define EXCEPT_OFFSET 0x800 + +/* DSP peripherals */ +#define DMAC0_OFFSET 0xFE000 +#define DMAC1_OFFSET 0xFF000 +#define DMAC_SIZE 0x420 +#define SSP0_OFFSET 0xFC000 +#define SSP1_OFFSET 0xFD000 +#define SSP_SIZE 0x100 + +#define HSW_STACK_DUMP_SIZE 32 + +#define HSW_PANIC_OFFSET(x) ((x) & 0xFFFF) + +static const struct snd_sof_debugfs_map hsw_debugfs[] = { + {"dmac0", HSW_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", HSW_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"ssp0", HSW_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", HSW_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"iram", HSW_DSP_BAR, IRAM_OFFSET, HSW_IRAM_SIZE}, + {"dram", HSW_DSP_BAR, DRAM_OFFSET, HSW_DRAM_SIZE}, + {"shim", HSW_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +/* + * Memory copy. + */ + +/* write has to deal with copying non 32 bit sized data */ +static void hsw_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy((void *)dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + } +} + +static void hsw_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +static void hsw_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +static void hsw_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Register IO + */ + +static void hsw_write(struct snd_sof_dev *sdev, void __iomem *addr, + u32 value) +{ + writel(value, addr); +} + +static u32 hsw_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +static void hsw_write64(struct snd_sof_dev *sdev, void __iomem *addr, + u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +static u64 hsw_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * DSP Control. + */ + +static int hsw_run(struct snd_sof_dev *sdev) +{ + /* set oportunistic mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, 0); + + /* set DSP to RUN */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_STALL, 0x0); + + return 0; //TODO: Fix return value +} + +static int hsw_reset(struct snd_sof_dev *sdev) +{ + /* put DSP into reset and stall */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_RST | SHIM_CSR_STALL); + + /* keep in reset for 10ms */ + mdelay(10); + + /* take DSP out of reset and keep stalled for FW loading */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_STALL); + + return 0; //TODO: Fix return value +} + +static int hsw_set_dsp_D0(struct snd_sof_dev *sdev) +{ + int tries = 10; + u32 reg; + + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | PCI_VDRTCL2_DTCGE, + 0); + + /* Disable D3PG (VDRTCTL0.D3PGD = 1) */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL0, + PCI_VDRTCL0_D3PGD, PCI_VDRTCL0_D3PGD); + + /* Set D0 state */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_PMCS, + PCI_PMCS_PS_MASK, 0); + + /* check that ADSP shim is enabled */ + while (tries--) { + reg = readl(sdev->bar[HSW_PCI_BAR] + PCI_PMCS) + & PCI_PMCS_PS_MASK; + if (reg == 0) + goto finish; + + msleep(20); + } + + return -ENODEV; + +finish: + /* + * select SSP1 19.2MHz base clock, SSP clock 0, + * turn off Low Power Clock + */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR, + SHIM_CSR_S1IOCS | SHIM_CSR_SBCS1 | + SHIM_CSR_LPCS, 0x0); + + /* stall DSP core, set clk to 192/96Mhz */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, + SHIM_CSR, SHIM_CSR_STALL | + SHIM_CSR_DCS_MASK, + SHIM_CSR_STALL | SHIM_CSR_DCS(4)); + + /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CLKCTL, + SHIM_CLKCTL_MASK | SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0, + SHIM_CLKCTL_MASK | SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0); + + /* Stall and reset core, set CSR */ + hsw_reset(sdev); + + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE); + + usleep_range(50, 55); + + /* switch on audio PLL */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_APLLSE_MASK, 0); + + /* + * set default power gating control, enable power gating control for + * all blocks. that is, can't be accessed, please enable each block + * before accessing. + */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_PCI_BAR, PCI_VDRTCTL0, + PCI_VDRTCL0_DSRAMPGE_MASK | + PCI_VDRTCL0_ISRAMPGE_MASK, 0); + + /* disable DMA finish function for SSP0 & SSP1 */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_CSR2, + SHIM_CSR2_SDFD_SSP1, + SHIM_CSR2_SDFD_SSP1); + + /* set on-demond mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH); + + /* Enable Interrupt from both sides */ + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_IMRX, + (SHIM_IMRX_BUSY | SHIM_IMRX_DONE), 0x0); + snd_sof_dsp_update_bits(sdev, HSW_DSP_BAR, SHIM_IMRD, + (SHIM_IMRD_DONE | SHIM_IMRD_BUSY | + SHIM_IMRD_SSP0 | SHIM_IMRD_DMAC), 0x0); + + /* clear IPC registers */ + snd_sof_dsp_write(sdev, HSW_DSP_BAR, SHIM_IPCX, 0x0); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, SHIM_IPCD, 0x0); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, 0x80, 0x6); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, 0xe0, 0x300a); + + return 0; +} + +static void hsw_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read regsisters */ + hsw_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* the get the stack */ + hsw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +static void hsw_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[HSW_STACK_DUMP_SIZE]; + u32 status, panic; + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); + hsw_get_registers(sdev, &xoops, stack, HSW_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + HSW_STACK_DUMP_SIZE); +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t hsw_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 isr; + int ret = IRQ_NONE; + + spin_lock(&sdev->hw_lock); + + /* Interrupt arrived, check src */ + isr = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_ISRX); + if (isr & SHIM_ISRX_DONE) { + /* Mask Done interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, + SHIM_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SHIM_ISRX_BUSY) { + /* Mask Busy interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, + SHIM_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + spin_unlock(&sdev->hw_lock); + return ret; +} + +static irqreturn_t hsw_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 ipcx, ipcd, hdr; + + ipcx = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); + + /* reply message from DSP */ + if (ipcx & SHIM_IPCX_DONE) { + /* Handle Immediate reply from DSP Core */ + hsw_mailbox_read(sdev, sdev->host_box.offset, &hdr, + sizeof(hdr)); + snd_sof_ipc_reply(sdev, hdr); + + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCX, + SHIM_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } + + ipcd = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCD); + + /* new message from DSP */ + if (ipcd & SHIM_IPCD_BUSY) { + /* Handle messages from DSP Core */ + if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, HSW_PANIC_OFFSET(ipcx) + + MBOX_OFFSET); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + } + + return IRQ_HANDLED; +} + +/* + * IPC Firmware ready. + */ +static void hsw_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = elem->offset + MBOX_OFFSET; + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = elem->offset + MBOX_OFFSET; + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = elem->offset + MBOX_OFFSET; + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + stream_offset, + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + MBOX_OFFSET; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HSW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +static int hsw_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = MBOX_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset %d\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + hsw_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + + snd_sof_dsp_mailbox_init(sdev, fw_ready->dspbox_offset, + fw_ready->dspbox_size, + fw_ready->hostbox_offset, + fw_ready->hostbox_size); + + dev_info(sdev->dev, + " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, MBOX_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + hsw_get_windows(sdev); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static int hsw_is_ready(struct snd_sof_dev *sdev) +{ + u32 val; + + val = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); + if (val & SHIM_IPCX_BUSY) + return 0; + + return 1; +} + +static int hsw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + /* send the message */ + hsw_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HSW_DSP_BAR, SHIM_IPCX, SHIM_IPCX_BUSY); + + return 0; +} + +static int hsw_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + hsw_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + hsw_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + return ret; +} + +static int hsw_cmd_done(struct snd_sof_dev *sdev) +{ + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCD, + SHIM_IPCD_BUSY | SHIM_IPCD_DONE, + SHIM_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + + return 0; +} + +/* + * Probe and remove. + */ +static int hsw_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct resource *mmio; + u32 base, size; + int ret = 0; + + /* LPE base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get LPE base at idx %d\n", + desc->resindex_lpe_base); + return -EINVAL; + } + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[HSW_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[HSW_DSP_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[HSW_DSP_BAR]); + + /* TODO: add offsets */ + sdev->mmio_bar = HSW_DSP_BAR; + sdev->mailbox_bar = HSW_DSP_BAR; + + /* PCI base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_pcicfg_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get PCI base at idx %d\n", + desc->resindex_pcicfg_base); + ret = -ENODEV; + goto pci_err; + } + + dev_dbg(sdev->dev, "PCI base at 0x%x size 0x%x", base, size); + sdev->bar[HSW_PCI_BAR] = ioremap(base, size); + if (!sdev->bar[HSW_PCI_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap PCI base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto pci_err; + } + dev_dbg(sdev->dev, "PCI VADDR %p\n", sdev->bar[HSW_PCI_BAR]); + + /* register our IRQ */ + sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + if (sdev->ipc_irq < 0) { + dev_err(sdev->dev, "error: failed to get IRQ at index %d\n", + desc->irqindex_host_ipc); + ret = sdev->ipc_irq; + goto irq_err; + } + + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, hsw_irq_handler, + hsw_irq_thread, 0, "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable the DSP SHIM */ + ret = hsw_set_dsp_D0(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DSP D0\n"); + return ret; + } + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* set BARS */ + sdev->cl_bar = HSW_DSP_BAR; + + /* set default mailbox */ + snd_sof_dsp_mailbox_init(sdev, MBOX_OFFSET, MBOX_SIZE, 0, 0); + + return ret; + +irq_err: + iounmap(sdev->bar[HSW_DSP_BAR]); +pci_err: + iounmap(sdev->bar[HSW_PCI_BAR]); + return ret; +} + +static int hsw_remove(struct snd_sof_dev *sdev) +{ + iounmap(sdev->bar[HSW_DSP_BAR]); + iounmap(sdev->bar[HSW_PCI_BAR]); + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +/* haswell ops */ +struct snd_sof_dsp_ops sof_hsw_ops = { + /*Device init */ + .probe = hsw_probe, + .remove = hsw_remove, + + /* DSP Core Control */ + .run = hsw_run, + .reset = hsw_reset, + + /* Register IO */ + .read = hsw_read, + .write = hsw_write, + .read64 = hsw_read64, + .write64 = hsw_write64, + + /* Block IO */ + .block_read = hsw_block_read, + .block_write = hsw_block_write, + + /* mailbox */ + .mailbox_read = hsw_mailbox_read, + .mailbox_write = hsw_mailbox_write, + + /* ipc */ + .send_msg = hsw_send_msg, + .get_reply = hsw_get_reply, + .fw_ready = hsw_fw_ready, + .is_ready = hsw_is_ready, + .cmd_done = hsw_cmd_done, + + /* debug */ + .debug_map = hsw_debugfs, + .debug_map_count = ARRAY_SIZE(hsw_debugfs), + .dbg_dump = hsw_dump, + + /* Module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + +}; +EXPORT_SYMBOL(sof_hsw_ops); + +MODULE_LICENSE("Dual BSD/GPL"); From 80c1ab57da6ead608e272542c7714789c2cb92ac Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:38:14 +0000 Subject: [PATCH 120/285] ASoC: SOF: Intel: Add support for BDW HW DSP support Add SOF support for Intel Broadwell based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/bdw.c | 753 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 753 insertions(+) create mode 100644 sound/soc/sof/intel/bdw.c diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c new file mode 100644 index 00000000000000..d1dbef55e27059 --- /dev/null +++ b/sound/soc/sof/intel/bdw.c @@ -0,0 +1,753 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardwre interface for audio DSP on Haswell and Broadwell + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../sof-priv.h" +#include "../ops.h" +#include "shim.h" + +/* BARs */ +#define BDW_DSP_BAR 0 +#define BDW_PCI_BAR 1 + +/* + * Debug + */ + +/* DSP memories for BDW */ +#define IRAM_OFFSET 0xA0000 +#define BDW_IRAM_SIZE (10 * 32 * 1024) +#define DRAM_OFFSET 0x00000 +#define BDW_DRAM_SIZE (20 * 32 * 1024) +#define SHIM_OFFSET 0xFB000 +#define SHIM_SIZE 0x100 +#define MBOX_OFFSET 0x9E000 +#define MBOX_SIZE 0x1000 +#define MBOX_DUMP_SIZE 0x30 +#define EXCEPT_OFFSET 0x800 + +/* DSP peripherals */ +#define DMAC0_OFFSET 0xFE000 +#define DMAC1_OFFSET 0xFF000 +#define DMAC_SIZE 0x420 +#define SSP0_OFFSET 0xFC000 +#define SSP1_OFFSET 0xFD000 +#define SSP_SIZE 0x100 + +#define BDW_STACK_DUMP_SIZE 32 + +#define BDW_PANIC_OFFSET(x) ((x) & 0xFFFF) + +static const struct snd_sof_debugfs_map bdw_debugfs[] = { + {"dmac0", BDW_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE}, + {"dmac1", BDW_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE}, + {"ssp0", BDW_DSP_BAR, SSP0_OFFSET, SSP_SIZE}, + {"ssp1", BDW_DSP_BAR, SSP1_OFFSET, SSP_SIZE}, + {"iram", BDW_DSP_BAR, IRAM_OFFSET, BDW_IRAM_SIZE}, + {"dram", BDW_DSP_BAR, DRAM_OFFSET, BDW_DRAM_SIZE}, + {"shim", BDW_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, +}; + +/* + * Memory copy. + */ + +/* write has to deal with copying non 32 bit sized data */ +static void bdw_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy((void *)dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + } +} + +static void bdw_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +static void bdw_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +static void bdw_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Register IO + */ + +static void bdw_write(struct snd_sof_dev *sdev, void __iomem *addr, + u32 value) +{ + writel(value, addr); +} + +static u32 bdw_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +static void bdw_write64(struct snd_sof_dev *sdev, void __iomem *addr, + u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +static u64 bdw_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * DSP Control. + */ + +static int bdw_run(struct snd_sof_dev *sdev) +{ + /* set oportunistic mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, 0); + + /* set DSP to RUN */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_STALL, 0x0); + + return 0; +} + +static int bdw_reset(struct snd_sof_dev *sdev) +{ + /* put DSP into reset and stall */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_RST | SHIM_CSR_STALL); + + /* keep in reset for 10ms */ + mdelay(10); + + /* take DSP out of reset and keep stalled for FW loading */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_RST | SHIM_CSR_STALL, + SHIM_CSR_STALL); + + return 0; +} + +static int bdw_set_dsp_D0(struct snd_sof_dev *sdev) +{ + int tries = 10; + u32 reg; + + /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE, 0); + + /* Disable D3PG (VDRTCTL0.D3PGD = 1) */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL0, + PCI_VDRTCL0_D3PGD, PCI_VDRTCL0_D3PGD); + + /* Set D0 state */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_PMCS, + PCI_PMCS_PS_MASK, 0); + + /* check that ADSP shim is enabled */ + while (tries--) { + reg = readl(sdev->bar[BDW_PCI_BAR] + PCI_PMCS) + & PCI_PMCS_PS_MASK; + if (reg == 0) + goto finish; + + msleep(20); + } + + return -ENODEV; + +finish: + /* + * select SSP1 19.2MHz base clock, SSP clock 0, + * turn off Low Power Clock + */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR, + SHIM_CSR_S1IOCS | SHIM_CSR_SBCS1 | + SHIM_CSR_LPCS, 0x0); + + /* stall DSP core, set clk to 192/96Mhz */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, + SHIM_CSR, SHIM_CSR_STALL | + SHIM_CSR_DCS_MASK, + SHIM_CSR_STALL | + SHIM_CSR_DCS(4)); + + /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CLKCTL, + SHIM_CLKCTL_MASK | + SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0, + SHIM_CLKCTL_MASK | + SHIM_CLKCTL_DCPLCG | + SHIM_CLKCTL_SCOE0); + + /* Stall and reset core, set CSR */ + bdw_reset(sdev); + + /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE, + PCI_VDRTCL2_DCLCGE | + PCI_VDRTCL2_DTCGE); + + usleep_range(50, 55); + + /* switch on audio PLL */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL2, + PCI_VDRTCL2_APLLSE_MASK, 0); + + /* + * set default power gating control, enable power gating control for + * all blocks. that is, can't be accessed, please enable each block + * before accessing. + */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_PCI_BAR, PCI_VDRTCTL0, + 0xfffffffC, 0x0); + + /* disable DMA finish function for SSP0 & SSP1 */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_CSR2, + SHIM_CSR2_SDFD_SSP1, + SHIM_CSR2_SDFD_SSP1); + + /* set on-demond mode on engine 0,1 for all channels */ + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_HMDC, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH, + SHIM_HMDC_HDDA_E0_ALLCH | + SHIM_HMDC_HDDA_E1_ALLCH); + + /* Enable Interrupt from both sides */ + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_IMRX, + (SHIM_IMRX_BUSY | SHIM_IMRX_DONE), 0x0); + snd_sof_dsp_update_bits(sdev, BDW_DSP_BAR, SHIM_IMRD, + (SHIM_IMRD_DONE | SHIM_IMRD_BUSY | + SHIM_IMRD_SSP0 | SHIM_IMRD_DMAC), 0x0); + + /* clear IPC registers */ + snd_sof_dsp_write(sdev, BDW_DSP_BAR, SHIM_IPCX, 0x0); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, SHIM_IPCD, 0x0); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, 0x80, 0x6); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, 0xe0, 0x300a); + + return 0; +} + +static void bdw_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read regsisters */ + bdw_mailbox_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* the get the stack */ + bdw_mailbox_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[BDW_STACK_DUMP_SIZE]; + u32 status, panic; + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); + bdw_get_registers(sdev, &xoops, stack, BDW_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + BDW_STACK_DUMP_SIZE); +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t bdw_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 isr; + int ret = IRQ_NONE; + + /* Interrupt arrived, check src */ + isr = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_ISRX); + if (isr & SHIM_ISRX_DONE) { + /* Mask Done interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, + SHIM_IMRX, SHIM_IMRX_DONE, + SHIM_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SHIM_ISRX_BUSY) { + /* Mask Busy interrupt before return */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, + SHIM_IMRX, SHIM_IMRX_BUSY, + SHIM_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +static irqreturn_t bdw_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 ipcx, ipcd, hdr; + + ipcx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); + + /* reply message from DSP */ + if (ipcx & SHIM_IPCX_DONE) { + /* Handle Immediate reply from DSP Core */ + bdw_mailbox_read(sdev, sdev->host_box.offset, &hdr, + sizeof(hdr)); + snd_sof_ipc_reply(sdev, hdr); + + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCX, + SHIM_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } + + ipcd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); + + /* new message from DSP */ + if (ipcd & SHIM_IPCD_BUSY) { + /* Handle messages from DSP Core */ + if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + + MBOX_OFFSET); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + } + + return IRQ_HANDLED; +} + +/* + * IPC Firmware ready. + */ +static void bdw_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = elem->offset + MBOX_OFFSET; + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = elem->offset + MBOX_OFFSET; + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = elem->offset + MBOX_OFFSET; + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + stream_offset, + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + MBOX_OFFSET; + snd_sof_debugfs_create_item(sdev, + sdev->bar[BDW_DSP_BAR] + + elem->offset + MBOX_OFFSET, + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +static int bdw_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = MBOX_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset %d\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + bdw_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + + snd_sof_dsp_mailbox_init(sdev, fw_ready->dspbox_offset, + fw_ready->dspbox_size, + fw_ready->hostbox_offset, + fw_ready->hostbox_size); + + dev_info(sdev->dev, + " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, MBOX_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + bdw_get_windows(sdev); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static int bdw_is_ready(struct snd_sof_dev *sdev) +{ + u32 val; + + val = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); + if (val & SHIM_IPCX_BUSY) + return 0; + + return 1; +} + +static int bdw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + /* send the message */ + bdw_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, BDW_DSP_BAR, SHIM_IPCX, SHIM_IPCX_BUSY); + + return 0; +} + +static int bdw_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + bdw_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + bdw_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + + return ret; +} + +static int bdw_cmd_done(struct snd_sof_dev *sdev) +{ + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCD, + SHIM_IPCD_BUSY | SHIM_IPCD_DONE, + SHIM_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + + return 0; +} + +/* + * Probe and remove. + */ +static int bdw_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + struct resource *mmio; + u32 base, size; + int ret = 0; + + /* LPE base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get LPE base at idx %d\n", + desc->resindex_lpe_base); + return -EINVAL; + } + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[BDW_DSP_BAR] = ioremap(base, size); + if (!sdev->bar[BDW_DSP_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap LPE base 0x%x size 0x%x\n", + base, size); + return -ENODEV; + } + dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BDW_DSP_BAR]); + + /* TODO: add offsets */ + sdev->mmio_bar = BDW_DSP_BAR; + sdev->mailbox_bar = BDW_DSP_BAR; + + /* PCI base */ + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_pcicfg_base); + if (mmio) { + base = mmio->start; + size = resource_size(mmio); + } else { + dev_err(sdev->dev, "error: failed to get PCI base at idx %d\n", + desc->resindex_pcicfg_base); + ret = -ENODEV; + goto pci_err; + } + + dev_dbg(sdev->dev, "PCI base at 0x%x size 0x%x", base, size); + sdev->bar[BDW_PCI_BAR] = ioremap(base, size); + if (!sdev->bar[BDW_PCI_BAR]) { + dev_err(sdev->dev, + "error: failed to ioremap PCI base 0x%x size 0x%x\n", + base, size); + ret = -ENODEV; + goto pci_err; + } + dev_dbg(sdev->dev, "PCI VADDR %p\n", sdev->bar[BDW_PCI_BAR]); + + /* register our IRQ */ + sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + if (sdev->ipc_irq < 0) { + dev_err(sdev->dev, "error: failed to get IRQ at index %d\n", + desc->irqindex_host_ipc); + ret = sdev->ipc_irq; + goto irq_err; + } + + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, bdw_irq_handler, + bdw_irq_thread, IRQF_SHARED, "AudioDSP", + sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* enable the DSP SHIM */ + ret = bdw_set_dsp_D0(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DSP D0\n"); + return ret; + } + + /* DSP DMA can only access low 31 bits of host memory */ + ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret); + return ret; + } + + /* set BARS */ + sdev->cl_bar = BDW_DSP_BAR; + + /* set default mailbox */ + snd_sof_dsp_mailbox_init(sdev, MBOX_OFFSET, MBOX_SIZE, 0, 0); + + return ret; + +irq_err: + iounmap(sdev->bar[BDW_DSP_BAR]); +pci_err: + iounmap(sdev->bar[BDW_PCI_BAR]); + return ret; +} + +static int bdw_remove(struct snd_sof_dev *sdev) +{ + iounmap(sdev->bar[BDW_DSP_BAR]); + iounmap(sdev->bar[BDW_PCI_BAR]); + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +/* broadwell ops */ +struct snd_sof_dsp_ops sof_bdw_ops = { + /*Device init */ + .probe = bdw_probe, + .remove = bdw_remove, + + /* DSP Core Control */ + .run = bdw_run, + .reset = bdw_reset, + + /* Register IO */ + .read = bdw_read, + .write = bdw_write, + .read64 = bdw_read64, + .write64 = bdw_write64, + + /* Block IO */ + .block_read = bdw_block_read, + .block_write = bdw_block_write, + + /* mailbox */ + .mailbox_read = bdw_mailbox_read, + .mailbox_write = bdw_mailbox_write, + + /* ipc */ + .send_msg = bdw_send_msg, + .get_reply = bdw_get_reply, + .fw_ready = bdw_fw_ready, + .is_ready = bdw_is_ready, + .cmd_done = bdw_cmd_done, + + /* debug */ + .debug_map = bdw_debugfs, + .debug_map_count = ARRAY_SIZE(bdw_debugfs), + .dbg_dump = bdw_dump, + + /* Module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(sof_bdw_ops); + +MODULE_LICENSE("Dual BSD/GPL"); From d3be805cf71b09efd9216dcdbc0edcc3581fc280 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:38:27 +0000 Subject: [PATCH 121/285] ASoC: SOF: Intel: Add APL/CNL HW DSP support Add SOF hardware DSP support for Intel Apollolake and Cannonlake based devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda.c | 531 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 504 ++++++++++++++++++++++++++++++++++++ 2 files changed, 1035 insertions(+) create mode 100644 sound/soc/sof/intel/hda.c create mode 100644 sound/soc/sof/intel/hda.h diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c new file mode 100644 index 00000000000000..de138cfaa08d85 --- /dev/null +++ b/sound/soc/sof/intel/hda.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * Register IO + */ + +void hda_dsp_write(struct snd_sof_dev *sdev, void __iomem *addr, u32 value) +{ + writel(value, addr); +} + +u32 hda_dsp_read(struct snd_sof_dev *sdev, void __iomem *addr) +{ + return readl(addr); +} + +void hda_dsp_write64(struct snd_sof_dev *sdev, void __iomem *addr, u64 value) +{ + memcpy_toio(addr, &value, sizeof(value)); +} + +u64 hda_dsp_read64(struct snd_sof_dev *sdev, void __iomem *addr) +{ + u64 val; + + memcpy_fromio(&val, addr, sizeof(val)); + return val; +} + +/* + * Memory copy. + */ + +void hda_dsp_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + void __iomem *dest = sdev->bar[sdev->mmio_bar] + offset; + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = size / 4; + n = size % 4; + + /* __iowrite32_copy use 32bit size values so divide by 4 */ + __iowrite32_copy((void *)dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + } +} + +void hda_dsp_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + void __iomem *src = sdev->bar[sdev->mmio_bar] + offset; + + memcpy_fromio(dest, src, size); +} + +/* + * Debug + */ + +struct hda_dsp_msg_code { + u32 code; + const char *msg; +}; + +static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = { + {HDA_DSP_ROM_FW_MANIFEST_LOADED, "status: manifest loaded"}, + {HDA_DSP_ROM_FW_FW_LOADED, "status: fw loaded"}, + {HDA_DSP_ROM_FW_ENTERED, "status: fw entered"}, + {HDA_DSP_ROM_CSE_ERROR, "error: cse error"}, + {HDA_DSP_ROM_CSE_WRONG_RESPONSE, "error: cse wrong response"}, + {HDA_DSP_ROM_IMR_TO_SMALL, "error: IMR too small"}, + {HDA_DSP_ROM_BASE_FW_NOT_FOUND, "error: base fw not found"}, + {HDA_DSP_ROM_CSE_VALIDATION_FAILED, "error: signature verification failed"}, + {HDA_DSP_ROM_IPC_FATAL_ERROR, "error: ipc fatal error"}, + {HDA_DSP_ROM_L2_CACHE_ERROR, "error: L2 cache error"}, + {HDA_DSP_ROM_LOAD_OFFSET_TO_SMALL, "error: load offset too small"}, + {HDA_DSP_ROM_API_PTR_INVALID, "error: API ptr invalid"}, + {HDA_DSP_ROM_BASEFW_INCOMPAT, "error: base fw incompatble"}, + {HDA_DSP_ROM_UNHANDLED_INTERRUPT, "error: unhandled interrupt"}, + {HDA_DSP_ROM_MEMORY_HOLE_ECC, "error: ECC memory hole"}, + {HDA_DSP_ROM_KERNEL_EXCEPTION, "error: kernel exception"}, + {HDA_DSP_ROM_USER_EXCEPTION, "error: user exception"}, + {HDA_DSP_ROM_UNEXPECTED_RESET, "error: unexpected reset"}, + {HDA_DSP_ROM_NULL_FW_ENTRY, "error: null FW entry point"}, +}; + +static void hda_dsp_get_status(struct snd_sof_dev *sdev) +{ + u32 status; + int i; + + status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS); + + for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) { + if (status == hda_dsp_rom_msg[i].code) { + dev_err(sdev->dev, "%s - code %8.8x\n", + hda_dsp_rom_msg[i].msg, status); + return; + } + } + + /* not for us, must be generic sof message */ + dev_dbg(sdev->dev, "unknown ROM status value %8.8x\n", status); +} + +static void hda_dsp_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + u32 *stack, size_t stack_words) +{ + /* first read registers */ + hda_dsp_block_read(sdev, sdev->dsp_oops_offset, xoops, sizeof(*xoops)); + + /* then get the stack */ + hda_dsp_block_read(sdev, sdev->dsp_oops_offset + sizeof(*xoops), stack, + stack_words * sizeof(u32)); +} + +void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + u32 stack[HDA_DSP_STACK_DUMP_SIZE]; + u32 status, panic; + + /* try APL specific status message types first */ + hda_dsp_get_status(sdev); + + /* now try generic SOF status messages */ + status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_FW_STATUS); + panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP); + + if (sdev->boot_complete) { + hda_dsp_get_registers(sdev, &xoops, stack, + HDA_DSP_STACK_DUMP_SIZE); + snd_sof_get_status(sdev, status, panic, &xoops, stack, + HDA_DSP_STACK_DUMP_SIZE); + } else { + dev_err(sdev->dev, "error: status = 0x%8.8x panic = 0x%8.8x\n", + status, panic); + hda_dsp_get_status(sdev); + } +} + +/* + * IPC Mailbox IO + */ + +void hda_dsp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_toio(dest, message, bytes); +} + +void hda_dsp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + memcpy_fromio(message, src, bytes); +} + +/* + * Supported devices. + */ + +static const struct sof_intel_dsp_desc chip_info[] = { +{ + /* Skylake */ + .id = 0x9d70, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_skl_ops, +}, +{ + /* Kabylake */ + .id = 0x9d71, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_skl_ops, +}, +{ + /* Apollolake - BXT-P */ + .id = 0x5a98, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_apl_ops, +}, +{ + /* BXT-M */ + .id = 0x1a98, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_apl_ops, +}, +{ + /* GeminiLake */ + .id = 0x3198, + .cores_num = 2, + .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .ipc_req = HDA_DSP_REG_HIPCI, + .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, + .ipc_ack = HDA_DSP_REG_HIPCIE, + .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, + .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .ops = &sof_apl_ops, +}, +{ + /* Cannonlake */ + .id = 0x9dc8, + .cores_num = 4, + .cores_mask = HDA_DSP_CORE_MASK(0) | + HDA_DSP_CORE_MASK(1) | + HDA_DSP_CORE_MASK(2) | + HDA_DSP_CORE_MASK(3), + .ipc_req = CNL_DSP_REG_HIPCIDR, + .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, + .ipc_ack = CNL_DSP_REG_HIPCIDA, + .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, + .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .ops = &sof_cnl_ops, +}, +}; + +static const struct sof_intel_dsp_desc *get_chip_info(int pci_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(chip_info); i++) { + if (chip_info[i].id == pci_id) + return &chip_info[i]; + } + + return NULL; +} + +/* + * We don't need to do a full HDA codec probe as external HDA codec mode is + * considered legacy and will not be supported under SOF. HDMI/DP HDA will + * be supported in the DSP. + */ +int hda_dsp_probe(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = sdev->pci; + struct sof_intel_hda_dev *hdev; + struct sof_intel_hda_stream *stream; + const struct sof_intel_dsp_desc *chip; + int i; + int ret = 0; + + chip = get_chip_info(pci->device); + if (!chip) { + dev_err(sdev->dev, "no such device supported, chip id:%x\n", + pci->device); + ret = -EIO; + goto err; + } + + hdev = devm_kzalloc(&pci->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + sdev->hda = hdev; + hdev->desc = chip; + + /* HDA base */ + sdev->bar[HDA_DSP_HDA_BAR] = pci_ioremap_bar(pci, HDA_DSP_HDA_BAR); + if (!sdev->bar[HDA_DSP_HDA_BAR]) { + dev_err(&pci->dev, "error: ioremap error\n"); + /* + * FIXME: why do we return directly, + * should we have a goto err here? + * or should all these gotos be replaced + * by a return? + */ + return -ENXIO; + } + + /* DSP base */ + sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR); + if (!sdev->bar[HDA_DSP_BAR]) { + dev_err(&pci->dev, "error: ioremap error\n"); + ret = -ENXIO; + goto err; + } + + sdev->mmio_bar = HDA_DSP_BAR; + sdev->mailbox_bar = HDA_DSP_BAR; + + pci_set_master(pci); + synchronize_irq(pci->irq); + + /* allow 64bit DMA address if supported by H/W */ + if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(64))) { + dev_dbg(&pci->dev, "DMA mask is 64 bit\n"); + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(64)); + } else { + dev_dbg(&pci->dev, "DMA mask is 32 bit\n"); + dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)); + } + + /* get controller capabilities */ + ret = hda_dsp_ctrl_get_caps(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to find DSP capability\n"); + goto err; + } + + /* init streams */ + ret = hda_dsp_stream_init(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to init streams\n"); + /* + * not all errors are due to memory issues, but trying + * to free everything does not harm + */ + goto stream_err; + } + + /* + * clear bits 0-2 of PCI register TCSEL (at offset 0x44) + * TCSEL == Traffic Class Select Register, which sets PCI express QOS + * Ensuring these bits are 0 clears playback static on some HD Audio + * codecs. PCI register TCSEL is defined in the Intel manuals. + */ + snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); + + /* + * while performing reset, controller may not come back properly causing + * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do + * reset (init chip) and then again set CGCTL.MISCBDCGE to 1 + */ + snd_sof_pci_update_bits(sdev, PCI_CGCTL, + PCI_CGCTL_MISCBDCGE_MASK, 0); + + /* clear WAKESTS */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK, + SOF_HDA_WAKESTS_INT_MASK); + + /* reset HDA controller */ + ret = hda_dsp_ctrl_link_reset(sdev); + if (ret < 0) { + dev_err(&pci->dev, "error: failed to reset HDA controller\n"); + goto stream_err; + } + + /* clear stream status */ + for (i = 0 ; i < hdev->num_capture ; i++) { + stream = &hdev->cstream[i]; + if (stream) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + } + + for (i = 0 ; i < hdev->num_playback ; i++) { + stream = &hdev->pstream[i]; + if (stream) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + } + + /* clear WAKESTS */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK, + SOF_HDA_WAKESTS_INT_MASK); + + /* clear interrupt status register */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM); + + /* enable CIE and GIE interrupts */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); + + dev_dbg(sdev->dev, "using PCI IRQ %d\n", pci->irq); + + /* register our IRQ */ + ret = request_threaded_irq(pci->irq, hda_dsp_stream_interrupt, + hda_dsp_stream_threaded_handler, + IRQF_SHARED, "AudioHDA", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n", + sdev->ipc_irq); + goto stream_err; + } + sdev->hda->irq = pci->irq; + + sdev->ipc_irq = pci->irq; + dev_dbg(sdev->dev, "using IPC IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_ipc_irq_handler, + chip->ops->irq_thread, IRQF_SHARED, + "AudioDSP", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IPC IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + /* re-enable CGCTL.MISCBDCGE after reset */ + snd_sof_pci_update_bits(sdev, PCI_CGCTL, + PCI_CGCTL_MISCBDCGE_MASK, + PCI_CGCTL_MISCBDCGE_MASK); + + device_disable_async_suspend(&pci->dev); + + /* enable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, SOF_HDA_PPCTL_GPROCEN); + + /* enable DSP IRQ */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_PIE, SOF_HDA_PPCTL_PIE); + + /* initialize waitq for code loading */ + init_waitqueue_head(&sdev->waitq); + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET; + + return 0; + +irq_err: + free_irq(pci->irq, sdev); +stream_err: + hda_dsp_stream_free(sdev); +err: + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + return ret; +} + +int hda_dsp_remove(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* disable DSP IRQ */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_PIE, 0); + + /* disable CIE and GIE interrupts */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, 0); + + /* disable cores */ + if (chip) + hda_dsp_core_reset_power_down(sdev, chip->cores_mask); + + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + + free_irq(sdev->ipc_irq, sdev); + free_irq(sdev->pci->irq, sdev); + + hda_dsp_stream_free(sdev); + return 0; +} + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h new file mode 100644 index 00000000000000..5eb677c601dd4f --- /dev/null +++ b/sound/soc/sof/intel/hda.h @@ -0,0 +1,504 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#ifndef __SOF_INTEL_HDA_H +#define __SOF_INTEL_HDA_H + +/* PCI registers */ +#define PCI_TCSEL 0x44 +#define PCI_CGCTL 0x48 + +/* PCI_CGCTL bits */ +#define PCI_CGCTL_MISCBDCGE_MASK BIT(6) + +/* Legacy HDA registers and bits used - widths are variable */ +#define SOF_HDA_GCAP 0x0 +#define SOF_HDA_GCTL 0x8 +/* accept unsol. response enable */ +#define SOF_HDA_GCTL_UNSOL BIT(8) +#define SOF_HDA_LLCH 0x14 +#define SOF_HDA_INTCTL 0x20 +#define SOF_HDA_INTSTS 0x24 +#define SOF_HDA_WAKESTS 0x0E +#define SOF_HDA_WAKESTS_INT_MASK ((1 << 8) - 1) + +/* SOF_HDA_GCTL register bist */ +#define SOF_HDA_GCTL_RESET BIT(0) + +/* SOF_HDA_INCTL and SOF_HDA_INTSTS regs */ +#define SOF_HDA_INT_GLOBAL_EN BIT(31) +#define SOF_HDA_INT_CTRL_EN BIT(30) +#define SOF_HDA_INT_ALL_STREAM 0xff + +#define SOF_HDA_MAX_CAPS 10 +#define SOF_HDA_CAP_ID_OFF 16 +#define SOF_HDA_CAP_ID_MASK (0xFFF << SOF_HDA_CAP_ID_OFF) +#define SOF_HDA_CAP_NEXT_MASK 0xFFFF + +#define SOF_HDA_PP_CAP_ID 0x3 +#define SOF_HDA_REG_PP_PPCH 0x10 +#define SOF_HDA_REG_PP_PPCTL 0x04 +#define SOF_HDA_PPCTL_PIE BIT(31) +#define SOF_HDA_PPCTL_GPROCEN BIT(30) + +#define SOF_HDA_SPIB_CAP_ID 0x4 +#define SOF_HDA_DRSM_CAP_ID 0x5 + +#define SOF_HDA_SPIB_BASE 0x08 +#define SOF_HDA_SPIB_INTERVAL 0x08 +#define SOF_HDA_SPIB_SPIB 0x00 +#define SOF_HDA_SPIB_MAXFIFO 0x04 + +#define SOF_HDA_PPHC_BASE 0x10 +#define SOF_HDA_PPHC_INTERVAL 0x10 + +#define SOF_HDA_PPLC_BASE 0x10 +#define SOF_HDA_PPLC_MULTI 0x10 +#define SOF_HDA_PPLC_INTERVAL 0x10 + +#define SOF_HDA_DRSM_BASE 0x08 +#define SOF_HDA_DRSM_INTERVAL 0x08 + +/* Descriptor error interrupt */ +#define SOF_HDA_CL_DMA_SD_INT_DESC_ERR 0x10 + +/* FIFO error interrupt */ +#define SOF_HDA_CL_DMA_SD_INT_FIFO_ERR 0x08 + +/* Buffer completion interrupt */ +#define SOF_HDA_CL_DMA_SD_INT_COMPLETE 0x04 + +#define SOF_HDA_CL_DMA_SD_INT_MASK \ + (SOF_HDA_CL_DMA_SD_INT_DESC_ERR | \ + SOF_HDA_CL_DMA_SD_INT_FIFO_ERR | \ + SOF_HDA_CL_DMA_SD_INT_COMPLETE) +#define SOF_HDA_SD_CTL_DMA_START 0x02 /* Stream DMA start bit */ + +/* Intel HD Audio Code Loader DMA Registers */ +#define SOF_HDA_ADSP_LOADER_BASE 0x80 +#define SOF_HDA_ADSP_DPLBASE 0x70 +#define SOF_HDA_ADSP_DPUBASE 0x74 +#define SOF_HDA_ADSP_DPLBASE_ENABLE 0x01 + +/* Stream Registers */ +#define SOF_HDA_ADSP_REG_CL_SD_CTL 0x00 +#define SOF_HDA_ADSP_REG_CL_SD_STS 0x03 +#define SOF_HDA_ADSP_REG_CL_SD_LPIB 0x04 +#define SOF_HDA_ADSP_REG_CL_SD_CBL 0x08 +#define SOF_HDA_ADSP_REG_CL_SD_LVI 0x0C +#define SOF_HDA_ADSP_REG_CL_SD_FIFOW 0x0E +#define SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE 0x10 +#define SOF_HDA_ADSP_REG_CL_SD_FORMAT 0x12 +#define SOF_HDA_ADSP_REG_CL_SD_FIFOL 0x14 +#define SOF_HDA_ADSP_REG_CL_SD_BDLPL 0x18 +#define SOF_HDA_ADSP_REG_CL_SD_BDLPU 0x1C + +/* CL: Software Position Based FIFO Capability Registers */ +#define SOF_DSP_REG_CL_SPBFIFO \ + (SOF_HDA_ADSP_LOADER_BASE + 0x20) +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCH 0x0 +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL 0x4 +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_SPIB 0x8 +#define SOF_HDA_ADSP_REG_CL_SPBFIFO_MAXFIFOS 0xc + +/* Stream Number */ +#define SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT 20 +#define SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK \ + (0xf << SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT) + +#define HDA_DSP_HDA_BAR 0 +#define HDA_DSP_PP_BAR 1 +#define HDA_DSP_SPIB_BAR 2 +#define HDA_DSP_DRSM_BAR 3 +#define HDA_DSP_BAR 4 + +#define SRAM_WINDOW_OFFSET(x) (0x80000 + x * 0x20000) + +#define HDA_DSP_MBOX_OFFSET SRAM_WINDOW_OFFSET(0) + +#define HDA_DSP_PANIC_OFFSET(x) \ + (((x) & 0xFFFFFF) + HDA_DSP_MBOX_OFFSET) + +/* SRAM window 0 FW "registers" */ +#define HDA_DSP_SRAM_REG_ROM_STATUS (HDA_DSP_MBOX_OFFSET + 0x0) +#define HDA_DSP_SRAM_REG_ROM_ERROR (HDA_DSP_MBOX_OFFSET + 0x4) +/* FW and ROM share offset 4 */ +#define HDA_DSP_SRAM_REG_FW_STATUS (HDA_DSP_MBOX_OFFSET + 0x4) +#define HDA_DSP_SRAM_REG_FW_TRACEP (HDA_DSP_MBOX_OFFSET + 0x8) +#define HDA_DSP_SRAM_REG_FW_END (HDA_DSP_MBOX_OFFSET + 0xc) + +#define HDA_DSP_MBOX_UPLINK_OFFSET 0x81000 + +#define HDA_DSP_STREAM_RESET_TIMEOUT 300 +#define HDA_DSP_CL_TRIGGER_TIMEOUT 300 + +#define HDA_DSP_SPIB_ENABLE 1 +#define HDA_DSP_SPIB_DISABLE 0 + +#define SOF_HDA_MAX_BUFFER_SIZE (32 * PAGE_SIZE) + +#define HDA_DSP_STACK_DUMP_SIZE 32 + +/* ROM status/error values */ +#define HDA_DSP_ROM_STS_MASK 0xf +#define HDA_DSP_ROM_INIT 0x1 +#define HDA_DSP_ROM_FW_MANIFEST_LOADED 0x3 +#define HDA_DSP_ROM_FW_FW_LOADED 0x4 +#define HDA_DSP_ROM_FW_ENTERED 0x5 +#define HDA_DSP_ROM_RFW_START 0xf +#define HDA_DSP_ROM_CSE_ERROR 40 +#define HDA_DSP_ROM_CSE_WRONG_RESPONSE 41 +#define HDA_DSP_ROM_IMR_TO_SMALL 42 +#define HDA_DSP_ROM_BASE_FW_NOT_FOUND 43 +#define HDA_DSP_ROM_CSE_VALIDATION_FAILED 44 +#define HDA_DSP_ROM_IPC_FATAL_ERROR 45 +#define HDA_DSP_ROM_L2_CACHE_ERROR 46 +#define HDA_DSP_ROM_LOAD_OFFSET_TO_SMALL 47 +#define HDA_DSP_ROM_API_PTR_INVALID 50 +#define HDA_DSP_ROM_BASEFW_INCOMPAT 51 +#define HDA_DSP_ROM_UNHANDLED_INTERRUPT 0xBEE00000 +#define HDA_DSP_ROM_MEMORY_HOLE_ECC 0xECC00000 +#define HDA_DSP_ROM_KERNEL_EXCEPTION 0xCAFE0000 +#define HDA_DSP_ROM_USER_EXCEPTION 0xBEEF0000 +#define HDA_DSP_ROM_UNEXPECTED_RESET 0xDECAF000 +#define HDA_DSP_ROM_NULL_FW_ENTRY 0x4c4c4e55 +#define HDA_DSP_IPC_PURGE_FW 0x01004000 + +/* various timeout values */ +#define HDA_DSP_PU_TIMEOUT 50 +#define HDA_DSP_PD_TIMEOUT 50 +#define HDA_DSP_RESET_TIMEOUT 50 +#define HDA_DSP_BASEFW_TIMEOUT 3000 +#define HDA_DSP_INIT_TIMEOUT 500 +#define HDA_DSP_CTRL_RESET_TIMEOUT 100 +#define HDA_DSP_WAIT_TIMEOUT 500 /* 500 msec */ + +#define HDA_DSP_ADSPIC_IPC 1 +#define HDA_DSP_ADSPIS_IPC 1 + +/* Intel HD Audio General DSP Registers */ +#define HDA_DSP_GEN_BASE 0x0 +#define HDA_DSP_REG_ADSPCS (HDA_DSP_GEN_BASE + 0x04) +#define HDA_DSP_REG_ADSPIC (HDA_DSP_GEN_BASE + 0x08) +#define HDA_DSP_REG_ADSPIS (HDA_DSP_GEN_BASE + 0x0C) +#define HDA_DSP_REG_ADSPIC2 (HDA_DSP_GEN_BASE + 0x10) +#define HDA_DSP_REG_ADSPIS2 (HDA_DSP_GEN_BASE + 0x14) + +/* Intel HD Audio Inter-Processor Communication Registers */ +#define HDA_DSP_IPC_BASE 0x40 +#define HDA_DSP_REG_HIPCT (HDA_DSP_IPC_BASE + 0x00) +#define HDA_DSP_REG_HIPCTE (HDA_DSP_IPC_BASE + 0x04) +#define HDA_DSP_REG_HIPCI (HDA_DSP_IPC_BASE + 0x08) +#define HDA_DSP_REG_HIPCIE (HDA_DSP_IPC_BASE + 0x0C) +#define HDA_DSP_REG_HIPCCTL (HDA_DSP_IPC_BASE + 0x10) + +/* HIPCI */ +#define HDA_DSP_REG_HIPCI_BUSY BIT(31) +#define HDA_DSP_REG_HIPCI_MSG_MASK 0x7FFFFFFF + +/* HIPCIE */ +#define HDA_DSP_REG_HIPCIE_DONE BIT(30) +#define HDA_DSP_REG_HIPCIE_MSG_MASK 0x3FFFFFFF + +/* HIPCCTL */ +#define HDA_DSP_REG_HIPCCTL_DONE BIT(1) +#define HDA_DSP_REG_HIPCCTL_BUSY BIT(0) + +/* HIPCT */ +#define HDA_DSP_REG_HIPCT_BUSY BIT(31) +#define HDA_DSP_REG_HIPCT_MSG_MASK 0x7FFFFFFF + +/* HIPCTE */ +#define HDA_DSP_REG_HIPCTE_MSG_MASK 0x3FFFFFFF + +#define HDA_DSP_ADSPIC_CL_DMA 0x2 +#define HDA_DSP_ADSPIS_CL_DMA 0x2 + +/* Delay before scheduling D0i3 entry */ +#define BXT_D0I3_DELAY 5000 + +#define FW_CL_STREAM_NUMBER 0x1 + +/* ADSPCS - Audio DSP Control & Status */ + +/* + * Core Reset - asserted high + * CRST Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_CRST_SHIFT 0 +#define HDA_DSP_ADSPCS_CRST_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CRST_SHIFT) + +/* + * Core run/stall - when set to '1' core is stalled + * CSTALL Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_CSTALL_SHIFT 8 +#define HDA_DSP_ADSPCS_CSTALL_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CSTALL_SHIFT) + +/* + * Set Power Active - when set to '1' turn cores on + * SPA Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_SPA_SHIFT 16 +#define HDA_DSP_ADSPCS_SPA_MASK(cm) ((cm) << HDA_DSP_ADSPCS_SPA_SHIFT) + +/* + * Current Power Active - power status of cores, set by hardware + * CPA Mask for a given core mask pattern, cm + */ +#define HDA_DSP_ADSPCS_CPA_SHIFT 24 +#define HDA_DSP_ADSPCS_CPA_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CPA_SHIFT) + +#define HDA_DSP_ADSPIC_CL_DMA 0x2 +#define HDA_DSP_ADSPIS_CL_DMA 0x2 + +/* Mask for a given core index, c = 0.. number of supported cores - 1 */ +#define HDA_DSP_CORE_MASK(c) BIT(c) + +/* + * Mask for a given number of cores + * nc = number of supported cores + */ +#define SOF_DSP_CORES_MASK(nc) GENMASK((nc - 1), 0) + +/* Intel HD Audio Inter-Processor Communication Registers for Cannonlake*/ +#define CNL_DSP_IPC_BASE 0xc0 +#define CNL_DSP_REG_HIPCTDR (CNL_DSP_IPC_BASE + 0x00) +#define CNL_DSP_REG_HIPCTDA (CNL_DSP_IPC_BASE + 0x04) +#define CNL_DSP_REG_HIPCTDD (CNL_DSP_IPC_BASE + 0x08) +#define CNL_DSP_REG_HIPCIDR (CNL_DSP_IPC_BASE + 0x10) +#define CNL_DSP_REG_HIPCIDA (CNL_DSP_IPC_BASE + 0x14) +#define CNL_DSP_REG_HIPCCTL (CNL_DSP_IPC_BASE + 0x28) + +/* HIPCI */ +#define CNL_DSP_REG_HIPCIDR_BUSY BIT(31) +#define CNL_DSP_REG_HIPCIDR_MSG_MASK 0x7FFFFFFF + +/* HIPCIE */ +#define CNL_DSP_REG_HIPCIDA_DONE BIT(31) +#define CNL_DSP_REG_HIPCIDA_MSG_MASK 0x7FFFFFFF + +/* HIPCCTL */ +#define CNL_DSP_REG_HIPCCTL_DONE BIT(1) +#define CNL_DSP_REG_HIPCCTL_BUSY BIT(0) + +/* HIPCT */ +#define CNL_DSP_REG_HIPCTDR_BUSY BIT(31) +#define CNL_DSP_REG_HIPCTDR_MSG_MASK 0x7FFFFFFF + +/* HIPCTDA */ +#define CNL_DSP_REG_HIPCTDA_DONE BIT(31) +#define CNL_DSP_REG_HIPCTDA_MSG_MASK 0x7FFFFFFF + +/* HIPCTDD */ +#define CNL_DSP_REG_HIPCTDD_MSG_MASK 0x7FFFFFFF + +/* BDL */ +#define HDA_DSP_BDL_SIZE 4096 +#define HDA_DSP_MAX_BDL_ENTRIES \ + (HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl)) + +struct sof_intel_dsp_bdl { + u32 addr_l; + u32 addr_h; + u32 size; + u32 ioc; +} __attribute((packed)); + +/* DSP hardware descriptor */ +struct sof_intel_dsp_desc { + int id; + int cores_num; + int cores_mask; + int ipc_req; + int ipc_req_mask; + int ipc_ack; + int ipc_ack_mask; + int ipc_ctl; + struct snd_sof_dsp_ops *ops; +}; + +/* per stream data for HDA DSP Frontend */ +struct sof_intel_hda_stream { + + /* addresses for stream HDA functions */ + void __iomem *pphc_addr; + void __iomem *pplc_addr; + void __iomem *spib_addr; + void __iomem *fifo_addr; + void __iomem *drsm_addr; + + /* runtime state */ + u32 dpib; + u32 lpib; + int tag; + int direction; + bool open; + bool running; + u32 index; + + /* buffer & descriptors */ + struct snd_dma_buffer bdl; + void __iomem *sd_addr; /* stream descriptor pointer */ + int sd_offset; /* Stream descriptor offset */ + unsigned int bufsize; /* size of the play buffer in bytes */ + unsigned int fifo_size; /* FIFO size */ + + __le32 *posbuf; /* position buffer pointer */ + unsigned int frags; /* number for period in the play buffer */ + unsigned int config; /* format config value */ + + /* PCM */ + struct snd_pcm_substream *substream; +}; + +#define SOF_HDA_PLAYBACK_STREAMS 16 +#define SOF_HDA_CAPTURE_STREAMS 16 +#define SOF_HDA_PLAYBACK 0 +#define SOF_HDA_CAPTURE 1 + +/* represents DSP HDA controller frontend - i.e. host facing control */ +struct sof_intel_hda_dev { + + /* hw config */ + const struct sof_intel_dsp_desc *desc; + + /* streams */ + struct sof_intel_hda_stream pstream[SOF_HDA_PLAYBACK_STREAMS]; + struct sof_intel_hda_stream cstream[SOF_HDA_CAPTURE_STREAMS]; + int num_capture; + int num_playback; + + /* position buffers */ + struct snd_dma_buffer posbuffer; + + /*trace */ + struct sof_intel_hda_stream *dtrace_stream; + + int irq; +}; + +/* + * DSP Core services. + */ +int hda_dsp_probe(struct snd_sof_dev *sdev); +int hda_dsp_remove(struct snd_sof_dev *sdev); +int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); +bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, + unsigned int core_mask); +int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, + unsigned int core_mask); +void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); + +/* + * DSP IO + */ +void hda_dsp_write(struct snd_sof_dev *sdev, void __iomem *addr, u32 value); +u32 hda_dsp_read(struct snd_sof_dev *sdev, void __iomem *addr); +void hda_dsp_write64(struct snd_sof_dev *sdev, void __iomem *addr, u64 value); +u64 hda_dsp_read64(struct snd_sof_dev *sdev, void __iomem *addr); +void hda_dsp_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size); +void hda_dsp_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size); +void hda_dsp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes); +void hda_dsp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes); + +/* + * DSP PCM Operations. + */ +int hda_dsp_pcm_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); +int hda_dsp_pcm_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); +int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd); + +/* + * DSP Stream Operations. + */ + +int hda_dsp_stream_init(struct snd_sof_dev *sdev); +void hda_dsp_stream_free(struct snd_sof_dev *sdev); +int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params); +int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, int cmd); +irqreturn_t hda_dsp_stream_interrupt(int irq, void *context); +irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); +int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + struct sof_intel_hda_stream *stream, + struct sof_intel_dsp_bdl *bdl, int size, + struct snd_pcm_hw_params *params); +struct sof_intel_hda_stream * + hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev); +struct sof_intel_hda_stream * + hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev); +int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int stream_tag); +int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int stream_tag); +int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + int enable, u32 size); + +/* + * DSP IPC Operations. + */ +int hda_dsp_ipc_is_ready(struct snd_sof_dev *sdev); +int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg); +int hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg); +int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id); +irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context); +irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context); +int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev); + +/* + * DSP Code loader. + */ +int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, const struct firmware *fw); +int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); + +/* + * HDA Controller Operations. + */ +int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev); +int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev); + +/* + * Trace Control. + */ +int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); +int hda_dsp_trace_release(struct snd_sof_dev *sdev); +int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); + +/* + * Platform Specific HW abstraction Ops. + */ +extern struct snd_sof_dsp_ops sof_apl_ops; +extern struct snd_sof_dsp_ops sof_cnl_ops; +extern struct snd_sof_dsp_ops sof_skl_ops; + +#endif From 7f48b6b01cc1834fc952e7a1507ff1be714192e6 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:11 +0100 Subject: [PATCH 122/285] ASoC: SOF: Intel: Add HDA controller for Intel DSP Support HDA controller operations for DSP and provide space for future DSP HDA FW integration. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-ctrl.c | 133 +++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 sound/soc/sof/intel/hda-ctrl.c diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c new file mode 100644 index 00000000000000..57b877bde8a8f4 --- /dev/null +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * HDA Operations. + */ + +int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev) +{ + unsigned long timeout; + u32 gctl = 0; + + /* reset the HDA controller */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL, + SOF_HDA_GCTL_RESET, 0); + + /* wait for reset */ + timeout = jiffies + msecs_to_jiffies(HDA_DSP_CTRL_RESET_TIMEOUT); + while (time_before(jiffies, timeout)) { + usleep_range(500, 1000); + gctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL); + if ((gctl & SOF_HDA_GCTL_RESET) == 0) + goto clear; + } + + /* reset failed */ + dev_err(sdev->dev, "error: failed to reset HDA controller gctl 0x%x\n", + gctl); + return -EIO; + +clear: + /* wait for codec */ + usleep_range(500, 1000); + + /* now take controller out of reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL, + SOF_HDA_GCTL_RESET, SOF_HDA_GCTL_RESET); + + /* wait for controller to be ready */ + timeout = jiffies + msecs_to_jiffies(HDA_DSP_CTRL_RESET_TIMEOUT); + while (time_before(jiffies, timeout)) { + gctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL); + if ((gctl & SOF_HDA_GCTL_RESET) == 1) + return 0; + usleep_range(500, 1000); + } + + /* reset failed */ + dev_err(sdev->dev, "error: failed to ready HDA controller gctl 0x%x\n", + gctl); + return -EIO; +} + +int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) +{ + u32 cap, offset, feature; + int ret = -ENODEV, count = 0; + + offset = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_LLCH); + + do { + cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); + + dev_dbg(sdev->dev, "checking for capabilities at offset 0x%x\n", + offset & SOF_HDA_CAP_NEXT_MASK); + + feature = (cap & SOF_HDA_CAP_ID_MASK) >> SOF_HDA_CAP_ID_OFF; + + switch (feature) { + case SOF_HDA_PP_CAP_ID: + dev_dbg(sdev->dev, "found DSP capability at 0x%x\n", + offset); + sdev->bar[HDA_DSP_PP_BAR] = sdev->bar[HDA_DSP_HDA_BAR] + + offset; + ret = 0; + break; + case SOF_HDA_SPIB_CAP_ID: + dev_dbg(sdev->dev, "found SPIB capability at 0x%x\n", + offset); + sdev->bar[HDA_DSP_SPIB_BAR] = + sdev->bar[HDA_DSP_HDA_BAR] + offset; + break; + case SOF_HDA_DRSM_CAP_ID: + dev_dbg(sdev->dev, "found DRSM capability at 0x%x\n", + offset); + sdev->bar[HDA_DSP_DRSM_BAR] = + sdev->bar[HDA_DSP_HDA_BAR] + offset; + break; + default: + dev_vdbg(sdev->dev, "found capability %d at 0x%x\n", + feature, offset); + break; + } + + offset = cap & SOF_HDA_CAP_NEXT_MASK; + } while (count++ <= SOF_HDA_MAX_CAPS && offset); + + return ret; +} + From 5d9b8b00d22fbe4156907c824db60b0e9f0673f3 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:12 +0100 Subject: [PATCH 123/285] ASoC: SOF: Intel: Add Intel specific HDA DSP HW operations Add support for various PM and core reset/run state transitions. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-dsp.c | 241 ++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 sound/soc/sof/intel/hda-dsp.c diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c new file mode 100644 index 00000000000000..82f9ce897137c5 --- /dev/null +++ b/sound/soc/sof/intel/hda-dsp.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * DSP Core control. + */ + +int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + u32 adspcs; + int ret; + + /* set reset bits for cores */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + HDA_DSP_ADSPCS_CRST_MASK(core_mask)); + + /* poll with timeout to check if operation successful */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + HDA_DSP_RESET_TIMEOUT); + + /* has core entered reset ? */ + adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS); + if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != + HDA_DSP_ADSPCS_CRST_MASK(core_mask)) { + dev_err(sdev->dev, + "error: reset enter failed: core_mask %x adspcs 0x%x\n", + core_mask, adspcs); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + u32 adspcs; + int ret; + + /* clear reset bits for cores */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), + 0); + + /* poll with timeout to check if operation successful */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CRST_MASK(core_mask), 0, + HDA_DSP_RESET_TIMEOUT); + + /* has core left reset ? */ + adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS); + if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != 0) { + dev_err(sdev->dev, + "error: reset leave failed: core_mask %x adspcs 0x%x\n", + core_mask, adspcs); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + /* stall core */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_HDA_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); + + /* set reset state */ + return hda_dsp_core_reset_enter(sdev, core_mask); +} + +int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + int ret; + + /* leave reset state */ + ret = hda_dsp_core_reset_leave(sdev, core_mask); + if (ret < 0) + return ret; + + /* run core */ + dev_dbg(sdev->dev, "unstall/run core: core_mask = %x\n", core_mask); + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + 0); + + /* is core now running ? */ + if (!hda_dsp_core_is_enabled(sdev, core_mask)) { + hda_dsp_core_stall_reset(sdev, core_mask); + dev_err(sdev->dev, "error: DSP start core failed: core_mask %x\n", + core_mask); + ret = -EIO; + } + + return ret; +} + +/* + * Power Management. + */ + +int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + u32 adspcs; + int ret; + + /* update bits */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_SPA_MASK(core_mask), + HDA_DSP_ADSPCS_SPA_MASK(core_mask)); + + /* poll with timeout to check if operation successful */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CPA_MASK(core_mask), + HDA_DSP_ADSPCS_CPA_MASK(core_mask), + HDA_DSP_PU_TIMEOUT); + if (ret < 0) + dev_err(sdev->dev, "error: timeout on core powerup\n"); + + /* did core power up ? */ + adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS); + if ((adspcs & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) != + HDA_DSP_ADSPCS_CPA_MASK(core_mask)) { + dev_err(sdev->dev, + "error: power up core failed core_mask %xadspcs 0x%x\n", + core_mask, adspcs); + ret = -EIO; + } + + return ret; +} + +int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + /* update bits */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_SPA_MASK(core_mask), 0); + + /* poll with timeout to check if operation successful */ + return snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPCS, HDA_DSP_ADSPCS_CPA_MASK(core_mask), 0, + HDA_DSP_PD_TIMEOUT); +} + +bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, + unsigned int core_mask) +{ + int val; + bool is_enable; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); + + is_enable = ((val & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) && + (val & HDA_DSP_ADSPCS_SPA_MASK(core_mask)) && + !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) && + !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask))); + + dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n", + is_enable, core_mask); + + return is_enable; +} + +int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, + unsigned int core_mask) +{ + int ret; + + /* place core in reset prior to power down */ + ret = hda_dsp_core_stall_reset(sdev, core_mask); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core reset failed: core_mask %x\n", + core_mask); + return ret; + } + + /* power down core */ + ret = hda_dsp_core_power_down(sdev, core_mask); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core power down fail mask %x: %d\n", + core_mask, ret); + return ret; + } + + /* make sure we are in OFF state */ + if (hda_dsp_core_is_enabled(sdev, core_mask)) { + dev_err(sdev->dev, "error: dsp core disable fail mask %x: %d\n", + core_mask, ret); + ret = -EIO; + } + + return ret; +} + From e10ad951c30386772fcd0cfefa946f46bb9f5437 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:13 +0100 Subject: [PATCH 124/285] ASoC: SOF: Intel: Add Intel specific HDA IPC mechanisms. Add HDA specific IPC mechanism for Intel DSP HW. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-ipc.c | 367 ++++++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 sound/soc/sof/intel/hda-ipc.c diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c new file mode 100644 index 00000000000000..357d909bfe80d6 --- /dev/null +++ b/sound/soc/sof/intel/hda-ipc.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev) +{ + /* tell DSP cmd is done - clear busy interrupt */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCT, + HDA_DSP_REG_HIPCT_BUSY, + HDA_DSP_REG_HIPCT_BUSY); + return 0; +} + +int hda_dsp_ipc_is_ready(struct snd_sof_dev *sdev) +{ + u64 val; + + /* is DSP ready for next IPC command */ + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); + if (val & HDA_DSP_REG_HIPCI_BUSY) + return 0; + + return 1; +} + +int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + u32 cmd = msg->header; + + /* send IPC message to DSP */ + hda_dsp_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, + cmd | HDA_DSP_REG_HIPCI_BUSY); + + return 0; +} + +int hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get IPC reply from DSP in the mailbox */ + hda_dsp_mailbox_read(sdev, sdev->host_box.offset, &reply, + sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%zx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + hda_dsp_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, size); + + return ret; +} + +/* IPC handler thread */ +irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 hipci, hipcie, hipct, hipcte, msg = 0, msg_ext = 0; + irqreturn_t ret = IRQ_NONE; + + /* here we handle IPC interrupts only */ + if (!(sdev->irq_status & HDA_DSP_ADSPIS_IPC)) + return ret; + + /* read IPC status */ + hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCIE); + hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); + + /* is this a reply message from the DSP */ + if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { + + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCI); + msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; + msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* mask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_DONE, 0); + + /* handle immediate reply from DSP core - ignore ROM messages */ + if (msg != 0x1004000) + snd_sof_ipc_reply(sdev, msg); + + /* clear DONE bit - tell DSP we have completed the operation */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCIE, + HDA_DSP_REG_HIPCIE_DONE, + HDA_DSP_REG_HIPCIE_DONE); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_DONE, + HDA_DSP_REG_HIPCCTL_DONE); + + ret = IRQ_HANDLED; + } + + /* is this a new message from DSP */ + if (hipct & HDA_DSP_REG_HIPCT_BUSY) { + + hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCTE); + msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; + msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* handle messages from DSP */ + if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + /* this is a PANIC message !! */ + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + } else { + /* normal message - process normally*/ + snd_sof_ipc_msgs_rx(sdev); + } + + /* clear busy interrupt */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCT, + HDA_DSP_REG_HIPCT_BUSY, + HDA_DSP_REG_HIPCT_BUSY); + ret = IRQ_HANDLED; + } + + if (ret == IRQ_HANDLED) { + /* reenable IPC interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + } + + /* wake up sleeper if we are loading code */ + if (sdev->code_loading) { + sdev->code_loading = 0; + wake_up(&sdev->waitq); + } + + return ret; +} + +/* is this IRQ for ADSP ? - we only care about IPC here */ +irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + int ret = IRQ_NONE; + + spin_lock(&sdev->hw_lock); + + /* store status */ + sdev->irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIS); + + /* invalid message ? */ + if (sdev->irq_status == 0xffffffff) + goto out; + + /* IPC message ? */ + if (sdev->irq_status & HDA_DSP_ADSPIS_IPC) { + /* disable IPC interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, 0); + ret = IRQ_WAKE_THREAD; + } + +out: + spin_unlock(&sdev->hw_lock); + return ret; +} + +/* + * IPC Firmware ready. + */ + +static void ipc_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "error: have no window info\n"); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = + elem->offset + SRAM_WINDOW_OFFSET(elem->id); + inbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + inbox_offset, + elem->size, "inbox"); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = + elem->offset + SRAM_WINDOW_OFFSET(elem->id); + outbox_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + outbox_offset, + elem->size, "outbox"); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "etrace"); + break; + case SOF_IPC_REGION_DEBUG: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "debug"); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = + elem->offset + SRAM_WINDOW_OFFSET(elem->id); + stream_size = elem->size; + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "stream"); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "regs"); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = elem->offset + + SRAM_WINDOW_OFFSET(elem->id); + snd_sof_debugfs_create_item(sdev, + sdev->bar[HDA_DSP_BAR] + + elem->offset + + SRAM_WINDOW_OFFSET + (elem->id), + elem->size, "exception"); + break; + default: + dev_err(sdev->dev, "error: get illegal window info\n"); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "error: get illegal mailbox window\n"); + return; + } + + snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, + outbox_offset, outbox_size); + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); +} + +int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + u32 offset; + + /* mailbox must be on 4k boundary */ + offset = HDA_DSP_MBOX_UPLINK_OFFSET; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", + msg_id, offset); + + /* copy data from the DSP FW ready offset */ + hda_dsp_block_read(sdev, offset, fw_ready, sizeof(*fw_ready)); + dev_info(sdev->dev, + " Firmware info: version %d.%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + /* now check for extended data */ + snd_sof_fw_parse_ext_data(sdev, HDA_DSP_MBOX_UPLINK_OFFSET + + sizeof(struct sof_ipc_fw_ready)); + + ipc_get_windows(sdev); + + return 0; +} From 8bb0c744392324d33dba4e72e302915feed5bbe9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:14 +0100 Subject: [PATCH 125/285] ASoC: SOF: Intel: Add Intel specific HDA firmware loader Add support for loading DSP firmware on Intel HDA based platforms. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-loader.c | 367 +++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 sound/soc/sof/intel/hda-loader.c diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c new file mode 100644 index 00000000000000..372ccfaac30647 --- /dev/null +++ b/sound/soc/sof/intel/hda-loader.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for HDA DSP code loader + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, + unsigned int size, struct snd_dma_buffer *dmab, + int direction) +{ + struct sof_intel_hda_stream *stream = NULL; + struct pci_dev *pci = sdev->pci; + int ret; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + stream = hda_dsp_stream_get_pstream(sdev); + } else { + dev_err(sdev->dev, "error: code loading DMA is playback only\n"); + return -EINVAL; + } + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + /* allocate DMA buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, &pci->dev, size, dmab); + if (ret < 0) { + dev_err(sdev->dev, "error: memory alloc failed: %x\n", ret); + goto error; + } + + stream->config = format; + stream->bufsize = size; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + goto error; + } + + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, size); + + return stream->tag; + +error: + hda_dsp_stream_put_pstream(sdev, stream->tag); + snd_dma_free_pages(dmab); + return ret; +} + +/* + * first boot sequence has some extra steps. core 0 waits for power + * status on core 1, so power up core 1 also momentarily, keep it in + * reset/stall and then turn it off + */ +static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, + u32 fwsize) +{ + int tag, ret, i; + u32 hipcie; + const struct sof_intel_dsp_desc *chip = sdev->hda->desc; + + /* prepare DMA for code loader stream */ + tag = cl_stream_prepare(sdev, 0x40, fwsize, &sdev->dmab, + SNDRV_PCM_STREAM_PLAYBACK); + + if (tag <= 0) { + dev_err(sdev->dev, "error: dma prepare for fw loading err: %x\n", + tag); + return tag; + } + + memcpy(sdev->dmab.area, fwdata, fwsize); + + /* step 1: power up corex */ + ret = hda_dsp_core_power_up(sdev, chip->cores_mask); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); + goto err; + } + + /* step 2: purge FW request */ + snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, + chip->ipc_req_mask | (HDA_DSP_IPC_PURGE_FW | + ((tag - 1) << 9))); + + /* step 3: unset core 0 reset state & unstall/run core 0 */ + ret = hda_dsp_core_run(sdev, HDA_DSP_CORE_MASK(0)); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core start failed %d\n", ret); + ret = -EIO; + goto err; + } + + /* step 4: wait for IPC DONE bit from ROM */ + for (i = HDA_DSP_INIT_TIMEOUT; i > 0; i--) { + hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + chip->ipc_ack); + + if (hipcie & chip->ipc_ack_mask) { + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + chip->ipc_ack, + chip->ipc_ack_mask, + chip->ipc_ack_mask); + goto step5; + } + mdelay(1); + } + + dev_err(sdev->dev, "error: waiting for HIPCIE done, reg: 0x%x\n", + hipcie); + goto err; + +step5: + /* step 5: power down corex */ + ret = hda_dsp_core_power_down(sdev, + chip->cores_mask & ~(HDA_DSP_CORE_MASK(0))); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core x power down failed\n"); + goto err; + } + + /* step 6: enable interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + + /* enable IPC DONE interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_DONE, + HDA_DSP_REG_HIPCCTL_DONE); + + /* enable IPC BUSY interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, + HDA_DSP_REG_HIPCCTL_BUSY, + HDA_DSP_REG_HIPCCTL_BUSY); + + /* step 7: wait for ROM init */ + ret = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS, + HDA_DSP_ROM_STS_MASK, HDA_DSP_ROM_INIT, + HDA_DSP_INIT_TIMEOUT); + if (ret >= 0) + goto out; + + ret = -EIO; + +err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + //sdev->dsp_ops.cleanup(sdev->dev, &sdev->dmab, tag); + hda_dsp_core_reset_power_down(sdev, HDA_DSP_CORE_MASK(0) | + HDA_DSP_CORE_MASK(1)); + return ret; + +out: + return tag; +} + +static int cl_trigger(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, int cmd) +{ + /* code loader is special case that reuses stream ops */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + wait_event_timeout(sdev->waitq, !sdev->code_loading, + HDA_DSP_CL_TRIGGER_TIMEOUT); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, 1 << stream->index); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->running = true; + return 0; + default: + return hda_dsp_stream_trigger(sdev, stream, cmd); + } +} + +static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, + struct sof_intel_hda_stream *stream) +{ + int ret; + + ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + + /* TODO: spin lock ?*/ + stream->open = 0; + stream->running = 0; + stream->substream = NULL; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, 0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, 0); + + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0); + snd_dma_free_pages(dmab); + dmab->area = NULL; + stream->bufsize = 0; + stream->config = 0; + + return ret; +} + +static int cl_copy_fw(struct snd_sof_dev *sdev, int tag) +{ + struct sof_intel_hda_stream *stream = NULL; + struct sof_intel_hda_dev *hdev = sdev->hda; + int ret, status, i; + + /* get stream with tag */ + for (i = 0; i < hdev->num_playback; i++) { + if (hdev->pstream[i].tag == tag) { + stream = &hdev->pstream[i]; + break; + } + } + if (!stream) { + dev_err(sdev->dev, + "error: could not get stream with stream tag%d\n", + tag); + return -ENODEV; + } + + ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, "error: DMA trigger start failed\n"); + return ret; + } + + status = snd_sof_dsp_register_poll(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS, + HDA_DSP_ROM_STS_MASK, + HDA_DSP_ROM_FW_ENTERED, + HDA_DSP_BASEFW_TIMEOUT); + + ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) { + dev_err(sdev->dev, "error: DMA trigger stop failed\n"); + return ret; + } + + ret = cl_cleanup(sdev, &sdev->dmab, stream); + if (ret < 0) { + dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); + return ret; + } + + return status; +} + +int hda_dsp_cl_load_fw(struct snd_sof_dev *sdev, const struct firmware *fw) +{ + struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); + int ret; + + /* set code loading condition to true */ + sdev->code_loading = 1; + + ret = request_firmware(&plat_data->fw, + plat_data->machine->sof_fw_filename, sdev->dev); + + if (ret < 0) { + dev_err(sdev->dev, "error: request firmware failed err: %d\n", + ret); + return -EINVAL; + } + + if (!plat_data->fw) + return -EINVAL; + + return ret; +} + +int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = dev_get_platdata(sdev->dev); + struct firmware stripped_firmware; + int ret, tag; + + stripped_firmware.data = plat_data->fw->data; + stripped_firmware.size = plat_data->fw->size; + + tag = cl_dsp_init(sdev, stripped_firmware.data, + stripped_firmware.size); + + /* retry enabling core and ROM load. seemed to help */ + if (tag < 0) { + tag = cl_dsp_init(sdev, stripped_firmware.data, + stripped_firmware.size); + if (tag <= 0) { + dev_err(sdev->dev, "Error code=0x%x: FW status=0x%x\n", + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_ERROR), + snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_SRAM_REG_ROM_STATUS)); + dev_err(sdev->dev, "Core En/ROM load fail:%d\n", + tag); + ret = tag; + goto irq_err; + } + } + + /* init for booting wait */ + init_waitqueue_head(&sdev->boot_wait); + sdev->boot_complete = false; + + /* at this point DSP ROM has been initialized and should be ready for + * code loading and firmware boot + */ + ret = cl_copy_fw(sdev, tag); + if (ret < 0) { + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); + goto irq_err; + } + + dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); + + return ret; + +irq_err: + hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); + + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); + dev_err(sdev->dev, "error: load fw failed err: %d\n", ret); + return ret; +} From 36319f423eb2287b02b1b1f12848908e67e750d3 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:15 +0100 Subject: [PATCH 126/285] ASoC: SOF: Intel: Add Intel specific HDA PCM operations Add PCM operations for Intel HDA based DSPs. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-pcm.c | 177 ++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 sound/soc/sof/intel/hda-pcm.c diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c new file mode 100644 index 00000000000000..0abb2e5778cae2 --- /dev/null +++ b/sound/soc/sof/intel/hda-pcm.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +#define SDnFMT_BASE(x) (x << 14) +#define SDnFMT_MULT(x) ((x - 1) << 11) +#define SDnFMT_DIV(x) ((x - 1) << 8) +#define SDnFMT_BITS(x) (x << 4) +#define SDnFMT_CHAN(x) (x << 0) + +static inline u32 get_mult_div(struct snd_sof_dev *sdev, int rate) +{ + switch (rate) { + case 8000: + return SDnFMT_DIV(6); + case 9600: + return SDnFMT_DIV(5); + case 11025: + return SDnFMT_BASE(1) | SDnFMT_DIV(4); + case 16000: + return SDnFMT_DIV(3); + case 22050: + return SDnFMT_BASE(1) | SDnFMT_DIV(2); + case 32000: + return SDnFMT_DIV(3) | SDnFMT_MULT(2); + case 44100: + return SDnFMT_BASE(1); + case 48000: + return 0; + case 88200: + return SDnFMT_BASE(1) | SDnFMT_MULT(2); + case 96000: + return SDnFMT_MULT(2); + case 176400: + return SDnFMT_BASE(1) | SDnFMT_MULT(4); + case 192000: + return SDnFMT_MULT(4); + default: + dev_warn(sdev->dev, "can't find div rate %d using 48kHz\n", + rate); + return 0; /* use 48KHz if not found */ + } +}; + +static inline u32 get_bits(struct snd_sof_dev *sdev, int sample_bits) +{ + switch (sample_bits) { + case 8: + return SDnFMT_BITS(0); + case 16: + return SDnFMT_BITS(1); + case 20: + return SDnFMT_BITS(2); + case 24: + return SDnFMT_BITS(3); + case 32: + return SDnFMT_BITS(4); + default: + dev_warn(sdev->dev, "can't find %d bits using 16bit\n", + sample_bits); + return SDnFMT_BITS(1); /* use 16bits format if not found */ + } +}; + +int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + struct snd_dma_buffer *dmab; + int ret; + u32 size, rate, bits; + + size = params_buffer_bytes(params); + rate = get_mult_div(sdev, params_rate(params)); + bits = get_bits(sdev, params_width(params)); + + stream->substream = substream; + + dmab = substream->runtime->dma_buffer_p; + + stream->config = rate | bits | (params_channels(params) - 1); + stream->bufsize = size; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + return ret; + } + + /* disable SPIB, to enable buffer wrap for stream */ + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + + return stream->tag; +} + +int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + + return hda_dsp_stream_trigger(sdev, stream, cmd); +} + +int hda_dsp_pcm_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct sof_intel_hda_stream *stream; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + stream = hda_dsp_stream_get_pstream(sdev); + else + stream = hda_dsp_stream_get_cstream(sdev); + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + /* binding pcm substream to hda stream */ + substream->runtime->private_data = stream; + return 0; +} + +int hda_dsp_pcm_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = hda_dsp_stream_put_pstream(sdev, stream->tag); + else + ret = hda_dsp_stream_put_cstream(sdev, stream->tag); + + if (ret) { + dev_dbg(sdev->dev, "stream %s not opened!\n", substream->name); + return -ENODEV; + } + + /* unbinding pcm substream to hda stream */ + substream->runtime->private_data = NULL; + return 0; +} + From c1946110ae976f6bdcfb056f750f2478f187c9de Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:16 +0100 Subject: [PATCH 127/285] ASoC: SOF: Intel: Add Intel specific HDA stream operations Add support or HDA DSP stream operations for Intel HDA DSPs. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-stream.c | 656 +++++++++++++++++++++++++++++++ 1 file changed, 656 insertions(+) create mode 100644 sound/soc/sof/intel/hda-stream.c diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c new file mode 100644 index 00000000000000..a9e81a505aa6c6 --- /dev/null +++ b/sound/soc/sof/intel/hda-stream.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +/* + * set up Buffer Descriptor List (BDL) for host memory transfer + * BDL describes the location of the individual buffers and is little endian. + */ +int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + struct sof_intel_hda_stream *stream, + struct sof_intel_dsp_bdl *bdl, int size, + struct snd_pcm_hw_params *params) +{ + int offset = 0; + int chunk = PAGE_SIZE, entry_size; + dma_addr_t addr; + + if (stream->substream && params) { + chunk = params_period_bytes(params); + dev_dbg(sdev->dev, "period_bytes:0x%x\n", chunk); + } + + while (size > 0) { + if (stream->frags >= HDA_DSP_MAX_BDL_ENTRIES) { + dev_err(sdev->dev, "error: stream frags exceeded\n"); + return -EINVAL; + } + + addr = snd_sgbuf_get_addr(dmab, offset); + + /* program BDL addr */ + bdl->addr_l = lower_32_bits(addr); + bdl->addr_h = upper_32_bits(addr); + + entry_size = size > chunk ? chunk : size; + + /* program BDL size */ + bdl->size = snd_sgbuf_get_chunk_size(dmab, offset, entry_size); + + /* program the IOC to enable interrupt + * when the whole fragment is processed + */ + size -= entry_size; + if (size) + bdl->ioc = 0; + else + bdl->ioc = 1; + + stream->frags++; + offset += bdl->size; + + dev_vdbg(sdev->dev, "bdl, frags:%d, entry size:0x%x;\n", + stream->frags, entry_size); + + bdl++; + } + + return offset; +} + +int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + int enable, u32 size) +{ + u32 mask = 0; + + if (!sdev->bar[HDA_DSP_SPIB_BAR]) { + dev_err(sdev->dev, "error: address of spib capability is NULL\n"); + return -EINVAL; + } + + mask |= (1 << stream->index); + + /* enable/disable SPIB for the stream */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, + enable << stream->index); + + /* set the SPIB value */ + hda_dsp_write(sdev, stream->spib_addr, size); + + return 0; +} + +/* get next unused playback stream */ +struct sof_intel_hda_stream * +hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream = NULL; + int i; + + /* get an unused playback stream */ + for (i = 0; i < hdev->num_playback; i++) { + if (!hdev->pstream[i].open) { + hdev->pstream[i].open = true; + stream = &hdev->pstream[i]; + break; + } + } + + /* stream found ? */ + if (!stream) + dev_err(sdev->dev, "error: no free playback streams\n"); + + return stream; +} + +/* get next unused capture stream */ +struct sof_intel_hda_stream * +hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream = NULL; + int i; + + /* get an unused capture stream */ + for (i = 0; i < hdev->num_capture; i++) { + if (!hdev->cstream[i].open) { + hdev->cstream[i].open = true; + stream = &hdev->cstream[i]; + break; + } + } + + /* stream found ? */ + if (!stream) + dev_err(sdev->dev, "error: no free capture streams\n"); + + return stream; +} + +/* free playback stream */ +int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int tag) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + int i; + + /* find used playback stream */ + for (i = 0; i < hdev->num_playback; i++) { + if (hdev->pstream[i].open && + hdev->pstream[i].tag == tag) { + hdev->pstream[i].open = false; + return 0; + } + } + + dev_dbg(sdev->dev, "tag %d not opened!\n", tag); + return -ENODEV; +} + +/* free capture stream */ +int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int tag) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + int i; + + /* find used capture stream */ + for (i = 0; i < hdev->num_capture; i++) { + if (hdev->cstream[i].open && + hdev->cstream[i].tag == tag) { + hdev->cstream[i].open = false; + return 0; + } + } + + dev_dbg(sdev->dev, "tag %d not opened!\n", tag); + return -ENODEV; +} + +int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, int cmd) +{ + /* cmd must be for audio stream */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_START: + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, + 1 << stream->index); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->running = true; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, 0x0); + + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->running = false; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, 0x0); + break; + default: + dev_err(sdev->dev, "error: unknown command: %d\n", cmd); + return -EINVAL; + } + + return 0; +} + +/* + * prepare for common hdac registers settings, for both code loader + * and normal stream. + */ +int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, + struct sof_intel_hda_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_dsp_bdl *bdl; + int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + u32 val, mask; + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + /* decouple host and link DMA */ + mask = 0x1 << stream->index; + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask); + + if (!dmab) { + dev_err(sdev->dev, "error: no dma buffer allocated!\n"); + return -ENODEV; + } + + /* clear stream status */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_DMA_SD_INT_MASK | + SOF_HDA_SD_CTL_DMA_START, 0); + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + /* stream reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0x1, + 0x1); + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset); + if (val & 0x1) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "error: stream reset failed\n"); + return -ETIMEDOUT; + } + + timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, 0x1, + 0x0); + + /* wait for hardware to report that stream is out of reset */ + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset); + if ((val & 0x1) == 0) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "error: timeout waiting for stream reset\n"); + return -ETIMEDOUT; + } + + if (stream->posbuf) + *stream->posbuf = 0; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + 0x0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + 0x0); + + /* clear stream status */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_DMA_SD_INT_MASK | + SOF_HDA_SD_CTL_DMA_START, 0); + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + stream->frags = 0; + + bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area; + ret = hda_dsp_stream_setup_bdl(sdev, dmab, stream, bdl, + stream->bufsize, params); + if (ret < 0) { + dev_err(sdev->dev, "error: set up of BDL failed\n"); + return ret; + } + + /* set up stream descriptor for DMA */ + /* program stream tag */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK, + stream->tag << + SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT); + + /* program cyclic buffer length */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, + stream->bufsize); + + /* program stream format */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_FORMAT, + 0xffff, stream->config); + + /* program last valid index */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, + 0xffff, (stream->frags - 1)); + + /* program BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + (u32)stream->bdl.addr); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + upper_32_bits(stream->bdl.addr)); + + /* enable position buffer */ + if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) + & SOF_HDA_ADSP_DPLBASE_ENABLE)) + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, + (u32)hdev->posbuffer.addr | + SOF_HDA_ADSP_DPLBASE_ENABLE); + + /* set interrupt enable bits */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + /* read FIFO size */ + if (stream->direction == SNDRV_PCM_STREAM_PLAYBACK) { + stream->fifo_size = + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + stream->sd_offset + + SOF_HDA_ADSP_REG_CL_SD_FIFOSIZE); + stream->fifo_size &= 0xffff; + stream->fifo_size += 1; + } else { + stream->fifo_size = 0; + } + + return ret; +} + +irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 status; + + if (!pm_runtime_active(sdev->dev)) + return IRQ_NONE; + + status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + + if (status == 0 || status == 0xffffffff) + return IRQ_NONE; + + return status ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + +irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + struct sof_intel_hda_dev *hdev = sdev->hda; + u32 status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + u32 sd_status; + int i; + + /* check playback streams */ + for (i = 0; i < hdev->num_playback; i++) { + /* is IRQ for this stream ? */ + if (status & (1 << hdev->pstream[i].index)) { + sd_status = + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + hdev->pstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS) & + 0xff; + + dev_dbg(sdev->dev, "pstream %d status 0x%x\n", + i, sd_status); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + hdev->pstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + if (!hdev->pstream[i].substream || + !hdev->pstream[i].running || + (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) + continue; + } + } + + /* check capture streams */ + for (i = 0; i < hdev->num_capture; i++) { + /* is IRQ for this stream ? */ + if (status & (1 << hdev->cstream[i].index)) { + sd_status = + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + hdev->cstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS) & + 0xff; + + dev_dbg(sdev->dev, "cstream %d status 0x%x\n", + i, sd_status); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + hdev->cstream[i].sd_offset + + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_CL_DMA_SD_INT_MASK); + + if (!hdev->cstream[i].substream || + !hdev->cstream[i].running || + (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) + continue; + } + } + + return IRQ_HANDLED; +} + +int hda_dsp_stream_init(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream; + struct pci_dev *pci = sdev->pci; + int i, num_playback, num_capture, num_total, ret; + u32 gcap; + + gcap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCAP); + dev_dbg(sdev->dev, "hda global caps = 0x%x\n", gcap); + + /* get stream count from GCAP */ + num_capture = (gcap >> 8) & 0x0f; + num_playback = (gcap >> 12) & 0x0f; + num_total = num_playback + num_capture; + + hdev->num_capture = num_capture; + hdev->num_playback = num_playback; + + dev_dbg(sdev->dev, "detected %d playback and %d capture streams\n", + num_playback, num_capture); + + if (num_playback >= SOF_HDA_PLAYBACK_STREAMS) { + dev_err(sdev->dev, "error: too many playback streams %d\n", + num_playback); + return -EINVAL; + } + + if (num_capture >= SOF_HDA_CAPTURE_STREAMS) { + dev_err(sdev->dev, "error: too many capture streams %d\n", + num_playback); + return -EINVAL; + } + + /* mem alloc for the position buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 8, + &hdev->posbuffer); + if (ret < 0) { + dev_err(sdev->dev, "error: posbuffer dma alloc failed\n"); + return -ENOMEM; + } + + /* create capture streams */ + for (i = 0; i < num_capture; i++) { + stream = &hdev->cstream[i]; + + stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; + + stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + + SOF_HDA_PPLC_INTERVAL * i; + + /* do we support SPIB */ + if (sdev->bar[HDA_DSP_SPIB_BAR]) { + stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_SPIB; + + stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_MAXFIFO; + } + + /* do we support DRSM */ + if (sdev->bar[HDA_DSP_DRSM_BAR]) + stream->drsm_addr = sdev->bar[HDA_DSP_DRSM_BAR] + + SOF_HDA_DRSM_BASE + SOF_HDA_DRSM_INTERVAL * i; + + stream->sd_offset = 0x20 * i + SOF_HDA_ADSP_LOADER_BASE; + stream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + + stream->sd_offset; + + stream->tag = i + 1; + stream->open = false; + stream->running = false; + stream->direction = SNDRV_PCM_STREAM_CAPTURE; + stream->index = i; + + /* memory alloc for stream BDL */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + HDA_DSP_BDL_SIZE, &stream->bdl); + if (ret < 0) { + dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + return -ENOMEM; + } + stream->posbuf = (__le32 *)(hdev->posbuffer.area + + (stream->index) * 8); + } + + /* create playback streams */ + for (i = num_capture; i < num_total; i++) { + stream = &hdev->pstream[i - num_capture]; + + /* we always have DSP support */ + stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; + + stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + + SOF_HDA_PPLC_INTERVAL * i; + + /* do we support SPIB */ + if (sdev->bar[HDA_DSP_SPIB_BAR]) { + stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_SPIB; + + stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + + SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + + SOF_HDA_SPIB_MAXFIFO; + } + + /* do we support DRSM */ + if (sdev->bar[HDA_DSP_DRSM_BAR]) + stream->drsm_addr = sdev->bar[HDA_DSP_DRSM_BAR] + + SOF_HDA_DRSM_BASE + SOF_HDA_DRSM_INTERVAL * i; + + stream->sd_offset = 0x20 * i + SOF_HDA_ADSP_LOADER_BASE; + stream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + + stream->sd_offset; + stream->tag = i - num_capture + 1; + stream->open = false; + stream->running = false; + stream->direction = SNDRV_PCM_STREAM_PLAYBACK; + stream->index = i; + + /* mem alloc for stream BDL */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + HDA_DSP_BDL_SIZE, &stream->bdl); + if (ret < 0) { + dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + return -ENOMEM; + } + + stream->posbuf = (__le32 *)(hdev->posbuffer.area + + (stream->index) * 8); + } + + return 0; +} + +void hda_dsp_stream_free(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev = sdev->hda; + struct sof_intel_hda_stream *stream; + int i; + + /* free position buffer */ + if (hdev->posbuffer.area) + snd_dma_free_pages(&hdev->posbuffer); + + /* free capture streams */ + for (i = 0; i < hdev->num_capture; i++) { + stream = &hdev->cstream[i]; + + /* free bdl buffer */ + if (stream->bdl.area) + snd_dma_free_pages(&stream->bdl); + } + + /* free playback streams */ + for (i = 0; i < hdev->num_playback; i++) { + stream = &hdev->pstream[i]; + + /* free bdl buffer */ + if (stream->bdl.area) + snd_dma_free_pages(&stream->bdl); + } +} + From 0489f5af0a066a52f6ec9c08224a93bc2463456c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:17 +0100 Subject: [PATCH 128/285] ASoC: SOF: Intel: Add Intel specific HDA trace operations Add trace operations for Intel based HDA DSPs Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda-trace.c | 88 +++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 sound/soc/sof/intel/hda-trace.c diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c new file mode 100644 index 00000000000000..2b88768cb903bb --- /dev/null +++ b/sound/soc/sof/intel/hda-trace.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for generic Intel audio DSP HDA IP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_stream *stream = sdev->hda->dtrace_stream; + struct snd_dma_buffer *dmab = &sdev->dmatb; + int ret; + + stream->bufsize = sdev->dmatb.bytes; + + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + if (ret < 0) + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + + return ret; +} + +int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *tag) +{ + sdev->hda->dtrace_stream = hda_dsp_stream_get_cstream(sdev); + + if (!sdev->hda->dtrace_stream) { + dev_err(sdev->dev, + "error: no available capture stream for DMA trace\n"); + return -ENODEV; + } + + *tag = sdev->hda->dtrace_stream->tag; + + /* + * initialize capture stream, set BDL address and return corresponding + * stream tag which will be sent to the firmware by IPC message. + */ + return hda_dsp_trace_prepare(sdev); +} + +int hda_dsp_trace_release(struct snd_sof_dev *sdev) +{ + if (sdev->hda->dtrace_stream) { + sdev->hda->dtrace_stream->open = false; + hda_dsp_stream_put_cstream(sdev, + sdev->hda->dtrace_stream->tag); + sdev->hda->dtrace_stream = NULL; + return 0; + } + + dev_dbg(sdev->dev, "DMA trace stream is not opened!\n"); + return -ENODEV; +} + +int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd) +{ + return hda_dsp_stream_trigger(sdev, sdev->hda->dtrace_stream, cmd); +} From db1915c88f789390b8a81a00e597b3fc34e4d56e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 9 May 2018 17:14:18 +0100 Subject: [PATCH 129/285] ASoC: SOF: Intel: Add platform differentiation for SKL, APL and CNL Add platform differentiation operations for different Intel HDA DSP platforms. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/apl.c | 96 ++++++++++++++++ sound/soc/sof/intel/cnl.c | 224 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/skl.c | 101 +++++++++++++++++ 3 files changed, 421 insertions(+) create mode 100644 sound/soc/sof/intel/apl.c create mode 100644 sound/soc/sof/intel/cnl.c create mode 100644 sound/soc/sof/intel/skl.c diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c new file mode 100644 index 00000000000000..d36e30d2a8b388 --- /dev/null +++ b/sound/soc/sof/intel/apl.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for audio DSP on Apollolake and GeminiLake + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000}, + {"dsp", HDA_DSP_BAR, 0, 0x10000}, +}; + +/* apollolake ops */ +struct snd_sof_dsp_ops sof_apl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = hda_dsp_write, + .read = hda_dsp_read, + .write64 = hda_dsp_write64, + .read64 = hda_dsp_read64, + + /* Block IO */ + .block_read = hda_dsp_block_read, + .block_write = hda_dsp_block_write, + + /* doorbell */ + .irq_handler = hda_dsp_ipc_irq_handler, + .irq_thread = hda_dsp_ipc_irq_thread, + + /* mailbox */ + .mailbox_read = hda_dsp_mailbox_read, + .mailbox_write = hda_dsp_mailbox_write, + + /* ipc */ + .send_msg = hda_dsp_ipc_send_msg, + .get_reply = hda_dsp_ipc_get_reply, + .fw_ready = hda_dsp_ipc_fw_ready, + .is_ready = hda_dsp_ipc_is_ready, + .cmd_done = hda_dsp_ipc_cmd_done, + + /* debug */ + .debug_map = apl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(apl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + + /* stream callbacks */ + .host_stream_open = hda_dsp_pcm_open, + .host_stream_close = hda_dsp_pcm_close, + .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, + + /* firmware loading */ + .load_firmware = hda_dsp_cl_load_fw, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, +}; +EXPORT_SYMBOL(sof_apl_ops); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c new file mode 100644 index 00000000000000..80d6cc772fd74a --- /dev/null +++ b/sound/soc/sof/intel/cnl.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for audio DSP on Cannonlake. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000}, + {"dsp", HDA_DSP_BAR, 0, 0x10000}, +}; + +static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + u32 hipci, hipcida, hipctdr, hipctdd, msg = 0, msg_ext = 0; + irqreturn_t ret = IRQ_NONE; + + /* here we handle IPC interrupts only */ + if (!(sdev->irq_status & HDA_DSP_ADSPIS_IPC)) + return ret; + + hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA); + hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); + + /* reply message from DSP */ + if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) { + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCIDR); + msg_ext = hipci & CNL_DSP_REG_HIPCIDR_MSG_MASK; + msg = hipcida & CNL_DSP_REG_HIPCIDA_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* mask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, 0); + + /* handle immediate reply from DSP core */ + snd_sof_ipc_reply(sdev, msg); + + /* clear DONE bit - tell DSP we have completed the operation */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCIDA, + CNL_DSP_REG_HIPCIDA_DONE, + CNL_DSP_REG_HIPCIDA_DONE); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, + CNL_DSP_REG_HIPCCTL_DONE); + + ret = IRQ_HANDLED; + } + + hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); + + /* new message from DSP */ + if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) { + hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDD); + msg = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK; + msg_ext = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* handle messages from DSP */ + if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == + SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + + /* clear busy interrupt to tell dsp controller this */ + /* interrupt has been accepted, not trigger it again */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDR, + CNL_DSP_REG_HIPCTDR_BUSY, + CNL_DSP_REG_HIPCTDR_BUSY); + + ret = IRQ_HANDLED; + } + + if (ret == IRQ_HANDLED) { + /* reenable IPC interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + } + + if (sdev->code_loading) { + sdev->code_loading = 0; + wake_up(&sdev->waitq); + } + + return ret; +} + +static int cnl_ipc_cmd_done(struct snd_sof_dev *sdev) +{ + /* set done bit to ack dsp the msg has been processed */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDA, + CNL_DSP_REG_HIPCTDA_DONE, + CNL_DSP_REG_HIPCTDA_DONE); + + return 0; +} + +static int cnl_ipc_is_ready(struct snd_sof_dev *sdev) +{ + u64 val; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR); + if (val & CNL_DSP_REG_HIPCIDR_BUSY) + return 0; + + return 1; +} + +static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + u32 cmd = msg->header; + + /* send the message */ + hda_dsp_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, + cmd | CNL_DSP_REG_HIPCIDR_BUSY); + + return 0; +} + +/* cannonlake ops */ +struct snd_sof_dsp_ops sof_cnl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = hda_dsp_write, + .read = hda_dsp_read, + .write64 = hda_dsp_write64, + .read64 = hda_dsp_read64, + + /* Block IO */ + .block_read = hda_dsp_block_read, + .block_write = hda_dsp_block_write, + + /* doorbell */ + .irq_handler = hda_dsp_ipc_irq_handler, + .irq_thread = cnl_ipc_irq_thread, + + /* mailbox */ + .mailbox_read = hda_dsp_mailbox_read, + .mailbox_write = hda_dsp_mailbox_write, + + /* ipc */ + .send_msg = cnl_ipc_send_msg, + .get_reply = hda_dsp_ipc_get_reply, + .fw_ready = hda_dsp_ipc_fw_ready, + .is_ready = cnl_ipc_is_ready, + .cmd_done = cnl_ipc_cmd_done, + + /* debug */ + .debug_map = cnl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + + /* stream callbacks */ + .host_stream_open = hda_dsp_pcm_open, + .host_stream_close = hda_dsp_pcm_close, + .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, + + /* firmware loading */ + .load_firmware = hda_dsp_cl_load_fw, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, +}; +EXPORT_SYMBOL(sof_cnl_ops); diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c new file mode 100644 index 00000000000000..d4b6de626456b1 --- /dev/null +++ b/sound/soc/sof/intel/skl.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Liam Girdwood + * Ranjani Sridharan + * Jeeja KP + * Rander Wang + * Keyon Jie + */ + +/* + * Hardware interface for audio DSP on Skylake and Kabylake. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +static const struct snd_sof_debugfs_map skl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000}, + {"dsp", HDA_DSP_BAR, 0, 0x10000}, +}; + +int skl_run_firmware(struct snd_sof_dev *sdev) +{ + return 0; +} + +/* skylake ops */ +struct snd_sof_dsp_ops sof_skl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = hda_dsp_write, + .read = hda_dsp_read, + .write64 = hda_dsp_write64, + .read64 = hda_dsp_read64, + + /* Block IO */ + .block_read = hda_dsp_block_read, + .block_write = hda_dsp_block_write, + + /* doorbell */ + .irq_handler = hda_dsp_ipc_irq_handler, + .irq_thread = hda_dsp_ipc_irq_thread, + + /* mailbox */ + .mailbox_read = hda_dsp_mailbox_read, + .mailbox_write = hda_dsp_mailbox_write, + + /* ipc */ + .send_msg = hda_dsp_ipc_send_msg, + .get_reply = hda_dsp_ipc_get_reply, + .fw_ready = hda_dsp_ipc_fw_ready, + .is_ready = hda_dsp_ipc_is_ready, + .cmd_done = hda_dsp_ipc_cmd_done, + + /* debug */ + .debug_map = skl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(skl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + + /* stream callbacks */ + .host_stream_open = hda_dsp_pcm_open, + .host_stream_close = hda_dsp_pcm_close, + .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, + + /* firmware loading */ + .load_firmware = hda_dsp_cl_load_fw, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, +}; +EXPORT_SYMBOL(sof_skl_ops); From 843579f3cbe843843670ffd9b03414be16075e6b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:39:03 +0000 Subject: [PATCH 130/285] ASoC: SOF: Add userspace ABI support Add userspace ABI for audio userspace application IO outside of regular ALSA PCM and kcontrols. This is intended to be used to format coefficients and data for custom processing components. Signed-off-by: Liam Girdwood --- include/uapi/sound/sof-abi.h | 29 ++++++++++ include/uapi/sound/sof-eq.h | 102 ++++++++++++++++++++++++++++++++++ include/uapi/sound/sof-fw.h | 69 +++++++++++++++++++++++ include/uapi/sound/sof-tone.h | 28 ++++++++++ 4 files changed, 228 insertions(+) create mode 100644 include/uapi/sound/sof-abi.h create mode 100644 include/uapi/sound/sof-eq.h create mode 100644 include/uapi/sound/sof-fw.h create mode 100644 include/uapi/sound/sof-tone.h diff --git a/include/uapi/sound/sof-abi.h b/include/uapi/sound/sof-abi.h new file mode 100644 index 00000000000000..4e78fa14465e35 --- /dev/null +++ b/include/uapi/sound/sof-abi.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + */ + +#ifndef __INCLUDE_UAPI_ABI_H__ +#define __INCLUDE_UAPI_ABI_H__ + +#define SOF_ABI_VERSION 1 +#define SOF_ABI_MAGIC 0x00464F53 /* "SOF\0" */ + +/* + * Header for all non IPC ABI data. Identifies data type, size and ABI. + * Used by any bespoke component data structures or binary blobs. + */ + +struct sof_abi_hdr { + uint32_t magic; /* 'S', 'O', 'F', '\0' */ + uint32_t type; /* component specific type */ + uint32_t size; /* size in bytes of data excluding this struct */ + uint32_t abi; /* SOF ABI version */ + uint32_t comp_abi; /* component specific ABI version */ + char data[0]; +} __attribute__((packed)); + +#endif diff --git a/include/uapi/sound/sof-eq.h b/include/uapi/sound/sof-eq.h new file mode 100644 index 00000000000000..96aefd1e1950b9 --- /dev/null +++ b/include/uapi/sound/sof-eq.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Seppo Ingalsuo + */ + +#ifndef EQ_H +#define EQ_H + +/* FIR EQ type */ + +/* Component will reject non-matching configuration. The version number need + * to be incremented with any ABI changes in function fir_cmd(). + */ +#define SOF_EQ_FIR_ABI_VERSION 1 + +#define SOF_EQ_FIR_IDX_SWITCH 0 + +#define SOF_EQ_FIR_MAX_SIZE 4096 /* Max size allowed for coef data in bytes */ + +/* + * eq_fir_configuration data structure contains this information + * uint16_t channels_in_config + * This describes the number of channels in this EQ config data. It + * can be different from PLATFORM_MAX_CHANNELS. + * uint16_t number_of_responses + * 0=no responses, 1=one response defined, 2=two responses defined, etc. + * int16_t data[] + * assign_response[STREAM_MAX_CHANNELS] + * -1 = not defined, 0 = use first response, 1 = use 2nd response, etc. + * E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the + * same first defined response and leave channels 4-7 unequalized. + * coef_data[] + * Repeated data { filter_length, input_shift, output_shift, h[] } + * for every EQ response defined where vector h has filter_length + * number of coefficients. Coefficients in h[] are in Q1.15 format. + * E.g. 16384 (Q1.15) = 0.5. The shifts are number of right shifts. + */ + +struct sof_eq_fir_config { + uint16_t channels_in_config; + uint16_t number_of_responses; + int16_t data[]; +}; + +/* IIR EQ type */ + +/* Component will reject non-matching configuration. The version number need + * to be incremented with any ABI changes in function fir_cmd(). + */ +#define SOF_EQ_FIR_ABI_VERSION 1 + +#define SOF_EQ_IIR_IDX_SWITCH 0 + +#define SOF_EQ_IIR_MAX_SIZE 1024 /* Max size allowed for coef data in bytes */ + +/* eq_iir_configuration + * uint32_t channels_in_config + * This describes the number of channels in this EQ config data. It + * can be different from PLATFORM_MAX_CHANNELS. + * uint32_t number_of_responses_defined + * 0=no responses, 1=one response defined, 2=two responses defined, etc. + * int32_t data[] + * Data consist of two parts. First is the response assign vector that + * has length of channels_in_config. The latter part is coefficient + * data. + * uint32_t assign_response[channels_in_config] + * -1 = not defined, 0 = use first response, 1 = use 2nd, etc. + * E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the + * same first defined response and leave channels 4-7 unequalized. + * coefficient_data[] + * <1st EQ> + * uint32_t num_biquads + * uint32_t num_biquads_in_series + * <1st biquad> + * int32_t coef_a2 Q2.30 format + * int32_t coef_a1 Q2.30 format + * int32_t coef_b2 Q2.30 format + * int32_t coef_b1 Q2.30 format + * int32_t coef_b0 Q2.30 format + * int32_t output_shift number of shifts right, shift left is negative + * int32_t output_gain Q2.14 format + * <2nd biquad> + * ... + * <2nd EQ> + * + * Note: A flat response biquad can be made with a section set to + * b0 = 1.0, gain = 1.0, and other parameters set to 0 + * {0, 0, 0, 0, 1073741824, 0, 16484} + */ + +struct sof_eq_iir_config { + uint32_t channels_in_config; + uint32_t number_of_responses; + int32_t data[]; +}; + +#endif /* EQ_H */ diff --git a/include/uapi/sound/sof-fw.h b/include/uapi/sound/sof-fw.h new file mode 100644 index 00000000000000..2f9bd827d2f02d --- /dev/null +++ b/include/uapi/sound/sof-fw.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Firmware file format . + */ + +#ifndef __INCLUDE_UAPI_SOF_FW_H__ +#define __INCLUDE_UAPI_SOF_FW_H__ + +#define SND_SOF_FW_SIG_SIZE 4 +#define SND_SOF_FW_ABI 1 +#define SND_SOF_FW_SIG "Reef" + +/* + * Firmware module is made up of 1 . N blocks of different types. The + * Block header is used to determine where and how block is to be copied in the + * DSP/host memory space. + */ +enum snd_sof_fw_blk_type { + SOF_BLK_IMAGE = 0, /* whole image - parsed by ROMs */ + SOF_BLK_TEXT = 1, + SOF_BLK_DATA = 2, + SOF_BLK_CACHE = 3, + SOF_BLK_REGS = 4, + SOF_BLK_SIG = 5, + SOF_BLK_ROM = 6, + /* add new block types here */ +}; + +struct snd_sof_blk_hdr { + enum snd_sof_fw_blk_type type; + uint32_t size; /* bytes minus this header */ + uint32_t offset; /* offset from base */ +} __attribute__((packed)); + +/* + * Firmware file is made up of 1 .. N different modules types. The module + * type is used to determine how to load and parse the module. + */ +enum snd_sof_fw_mod_type { + SOF_FW_BASE = 0, /* base firmware image */ + SOF_FW_MODULE = 1, /* firmware module */ +}; + +struct snd_sof_mod_hdr { + enum snd_sof_fw_mod_type type; + uint32_t size; /* bytes minus this header */ + uint32_t num_blocks; /* number of blocks */ +} __attribute__((packed)); + +/* + * Firmware file header. + */ +struct snd_sof_fw_header { + unsigned char sig[SND_SOF_FW_SIG_SIZE]; /* "Reef" */ + uint32_t file_size; /* size of file minus this header */ + uint32_t num_modules; /* number of modules */ + uint32_t abi; /* version of header format */ +} __attribute__((packed)); + +#endif diff --git a/include/uapi/sound/sof-tone.h b/include/uapi/sound/sof-tone.h new file mode 100644 index 00000000000000..f9c9bf5c482d85 --- /dev/null +++ b/include/uapi/sound/sof-tone.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note)) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Seppo Ingalsuo + */ + +#ifndef TONE_H +#define TONE_H + +/* Component will reject non-matching configuration. The version number need + * to be incremented with any ABI changes in function fir_cmd(). + */ +#define SOF_TONE_ABI_VERSION 1 + +#define SOF_TONE_IDX_FREQUENCY 0 +#define SOF_TONE_IDX_AMPLITUDE 1 +#define SOF_TONE_IDX_FREQ_MULT 2 +#define SOF_TONE_IDX_AMPL_MULT 3 +#define SOF_TONE_IDX_LENGTH 4 +#define SOF_TONE_IDX_PERIOD 5 +#define SOF_TONE_IDX_REPEATS 6 +#define SOF_TONE_IDX_LIN_RAMP_STEP 7 + +#endif /* TONE_ABI_H */ From 4078be48bb29c524a888d1ab82c73044ddef9cfa Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:35:47 +0000 Subject: [PATCH 131/285] ASoC: SOF: Add VirtIO support Add support for running SOF on multiple VMs within the same HW. Signed-off-by: Liam Girdwood --- sound/soc/sof/virtio-be.c | 126 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/virtio-fe.c | 130 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 sound/soc/sof/virtio-be.c create mode 100644 sound/soc/sof/virtio-fe.c diff --git a/sound/soc/sof/virtio-be.c b/sound/soc/sof/virtio-be.c new file mode 100644 index 00000000000000..4c07caeba41b0c --- /dev/null +++ b/sound/soc/sof/virtio-be.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Luo Xionghu + * Liam Girdwood + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" +#include "ops.h" + +/* BE driver + * + * This driver will create IO Queues for communition from FE drivers. + * The FE driver will send real IPC structures over the queue and then + * the BE driver will send the structures directlt to the DSP. The BE will + * get the IPC reply from the DSP and send it back to the FE over the queue. + * + * The virt IO message Q handlers in this file will :- + * + * 1) Check that the message is valid and not for any componenets that don't + * belong to the guest. + * + * 2) Call snd_sof_dsp_tx_msg(struct snd_sof_dev *sdev, + * struct snd_sof_ipc_msg *msg) to send the message to the DSP. + * + * Replies will be sent back using a similar method. + */ + +static int sof_virtio_validate(struct virtio_device *dev) +{ + /* do we need this func ?? */ + return 0; +} + +static int sof_virtio_probe(struct virtio_device *dev) +{ + /* register fe device with sof core */ + //snd_sof_virtio_register_fe(dev); + + /* create our virtqueues */s + + /* send topology data to fe via virtq */ + + return 0; +} + +static void sof_virtio_remove(struct virtio_device *dev) +{ + /* remove topology from fe via virtqueue */ + + /* destroy virtqueue */ +} + +#ifdef CONFIG_PM +static int sof_virtio_freeze(struct virtio_device *dev) +{ + /* pause and suspend any streams for this FE */ + return 0; +} + +static int sof_virtio_restore(struct virtio_device *dev) +{ + /* restore and unpause any streams for this FE */ + return 0; +} +#endif + +/* IDs of FEs */ +static const struct virtio_device_id *fe_id_table[] + { +}; + +static struct virtio_driver sof_be_virtio_driver = { + .driver = { + .name = "sof-virtio-be", + .owner = THIS_MODULE, + }, + + .id_table = fe_id_table, + + //const unsigned int *feature_table; + //unsigned int feature_table_size; + //const unsigned int *feature_table_legacy; + //unsigned int feature_table_size_legacy; + + validate = sof_virtio_validate, + probe = sof_virtio_probe, + remove = sof_virtio_remove, + +#ifdef CONFIG_PM + freeze = sof_virtio_freeze, + restore = sof_virtio_restore, +#endif +}; + +/* this will be called by sof core when core is ready */ +int sof_virtio_register(struct snd_sof_dev *sdev) +{ + int ret; + + ret = register_virtio_driver(&sof_be_virtio_driver); + /* do we need to do anythig else here */ + return ret; +} + +/* called by sof core when driver is removed */ +void sof_virtio_unregister(struct snd_sof_dev *sdev) +{ + unregister_virtio_driver(&sof_be_virtio_driver); + /* do we need to do anythig else here */ +} diff --git a/sound/soc/sof/virtio-fe.c b/sound/soc/sof/virtio-fe.c new file mode 100644 index 00000000000000..1019e65876b923 --- /dev/null +++ b/sound/soc/sof/virtio-fe.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Luo Xionghu + * Liam Girdwood + */ + +/* + * virt IO FE driver + * + * The SOF driver thinks this driver is another audio DSP, however the calls + * made by the SOF driver core do not directly go to HW, but over a virtIO + * message Q to the virtIO BE driver. + * + * The virtIO message Q will use the *exact* same IPC structures as we currently + * use in the mailbox. + * + * Guest OS SOF core -> SOF FE -> virtIO Q -> SOF BE -> + * System OS SOF core -> DSP + * + * The mailbox IO and TX/RX msg functions below will do IO on the virt IO Q. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sof-priv.h" +#include "ops.h" +#include "intel.h" + +/* + * IPC Firmware ready. + */ +static int virtio_fe_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + /* not needed for FE ? */ + return 0; +} + +/* + * IPC Mailbox IO + */ + +static void virtio_fe_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + /* write data to message Q buffer before sending message */ +} + +static void virtio_fe_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + /* read data from message Q buffer after receiving message */ +} + +static int virtio_fe_tx_busy(struct snd_sof_dev *sdev) +{ + /* return 1 if tx message Q is busy */ +} + +static int virtio_fe_tx_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + /* write msg to the virtio queue message for BE */ + + return 0; +} + +static int virtio_fe_rx_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + /* read the virtio queue message from BE and copy to msg */ + return 0; +} + +/* + * Probe and remove. + */ + +static int virtio_fe_probe(struct snd_sof_dev *sdev) +{ + /* register virtio device */ + + /* conenct virt queues to BE */ +} + +static int virtio_fe_remove(struct snd_sof_dev *sdev) +{ + /* free virtio resurces and unregister device */ +} + +/* baytrail ops */ +struct snd_sof_dsp_ops snd_sof_virtio_fe_ops = { + /* device init */ + .probe = virtio_fe_probe, + .remove = virtio_fe_remove, + + /* mailbox */ + .mailbox_read = virtio_fe_mailbox_read, + .mailbox_write = virtio_fe_mailbox_write, + + /* ipc */ + .tx_msg = virtio_fe_tx_msg, + .rx_msg = virtio_fe_rx_msg, + .fw_ready = virtio_fe_fw_ready, + .tx_busy = virtio_fe_tx_busy, + + /* module loading */ +// .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(snd_sof_virtio_fe_ops); + +MODULE_LICENSE("Dual BSD/GPL"); From 361530c1f55d5339c5001790584cb14b275cee2a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:36:46 +0000 Subject: [PATCH 132/285] ASoC: SOF: Add SPI device support Add support for SPI based DSP devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/hw-spi.c | 250 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-spi-dev.c | 167 ++++++++++++++++++++++++ 2 files changed, 417 insertions(+) create mode 100644 sound/soc/sof/hw-spi.c create mode 100644 sound/soc/sof/sof-spi-dev.c diff --git a/sound/soc/sof/hw-spi.c b/sound/soc/sof/hw-spi.c new file mode 100644 index 00000000000000..e20682da09202c --- /dev/null +++ b/sound/soc/sof/hw-spi.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +/* + * Hardware interface for audio DSPs via SPI + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sof-priv.h" +#include "ops.h" +#include "intel.h" + +/* + * Memory copy. + */ + +static void spi_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) +{ + // use spi_write() to copy data to DSP +} + +static void spi_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) +{ + // use spi_read() to copy data from DSP +} + +/* + * IPC Firmware ready. + */ +static int spi_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &fw_ready->version; + + dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x\n", msg_id); + + // read local buffer with SPI data + + dev_info(sdev->dev, " Firmware info: version %d:%d-%s build %d on %s:%s\n", + v->major, v->minor, v->tag, v->build, v->date, v->time); + + return 0; +} + +/* + * IPC Mailbox IO + */ + +static void spi_mailbox_write(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; + + //memcpy_toio(dest, message, bytes); + /* + * this will copy to a local memory buffer that will be sent to DSP via + * SPI at next IPC + */ +} + +static void spi_mailbox_read(struct snd_sof_dev *sdev, u32 offset, + void *message, size_t bytes) +{ + void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + + //memcpy_fromio(message, src, bytes); + /* + * this will copy from a local memory buffer that will be received from + * DSP via SPI at last IPC + */ +} + +/* + * IPC Doorbell IRQ handler and thread. + */ + +static irqreturn_t spi_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + int ret = IRQ_NONE; + + // on SPI based devices this will likely come via a SoC GPIO IRQ + + // check if GPIO is assetred and if so run thread. + + return ret; +} + +static irqreturn_t spi_irq_thread(int irq, void *context) +{ + struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + + // read SPI data into local buffer and determine IPC cmd or reply + + /* + * if reply. Handle Immediate reply from DSP Core and set DSP + * state to ready + */ + //snd_sof_ipc_reply(sdev, ipcx); + + /* if cmd, Handle messages from DSP Core */ + //snd_sof_ipc_msgs_rx(sdev); + + return IRQ_HANDLED; +} + +static int spi_is_ready(struct snd_sof_dev *sdev) +{ + // use local variable to store DSP command state. either DSP is ready + // for new cmd or still processing current cmd. + + return 1; +} + +static int spi_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + u64 cmd = msg->header; + + /* send the message */ + spi_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + + return 0; +} + +static int spi_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc_reply reply; + int ret = 0; + u32 size; + + /* get reply */ + spi_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + size = sizeof(reply); + ret = reply.error; + } else { + /* reply correct size ? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected 0x%lx got 0x%x bytes\n", + msg->reply_size, reply.hdr.size); + size = msg->reply_size; + ret = -EINVAL; + } else { + size = reply.hdr.size; + } + } + + /* read the message */ + if (msg->msg_data && size > 0) + spi_mailbox_read(sdev, sdev->host_box.offset, msg->reply_data, + size); + + return ret; +} + +/* + * Probe and remove. + */ + +static int spi_sof_probe(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_dev_desc *desc = pdata->desc; + struct platform_device *pdev = + container_of(sdev->parent, struct platform_device, dev); + int ret = 0; + + /* get IRQ from Device tree or ACPI - register our IRQ */ + dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); + ret = request_threaded_irq(sdev->ipc_irq, spi_irq_handler, + spi_irq_thread, IRQF_SHARED, "AudioDSP", + sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register IRQ %d\n", + sdev->ipc_irq); + goto irq_err; + } + + return ret; +} + +static int spi_sof_remove(struct snd_sof_dev *sdev) +{ + free_irq(sdev->ipc_irq, sdev); + return 0; +} + +/* baytrail ops */ +struct snd_sof_dsp_ops snd_sof_spi_ops = { + /* device init */ + .probe = spi_sof_probe, + .remove = spi_sof_remove, + + /* Block IO */ + .block_read = spi_block_read, + .block_write = spi_block_write, + + /* doorbell */ + .irq_handler = spi_irq_handler, + .irq_thread = spi_irq_thread, + + /* mailbox */ + .mailbox_read = spi_mailbox_read, + .mailbox_write = spi_mailbox_write, + + /* ipc */ + .send_msg = spi_send_msg, + .get_reply = spi_get_reply, + .fw_ready = spi_fw_ready, + .is_ready = spi_is_ready, + .cmd_done = spi_cmd_done, + + /* debug */ + .debug_map = spi_debugfs, + .debug_map_count = ARRAY_SIZE(spi_debugfs), + .dbg_dump = spi_dump, + + /* module loading */ + .load_module = snd_sof_parse_module_memcpy, + + /*Firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, +}; +EXPORT_SYMBOL(snd_sof_spi_ops); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c new file mode 100644 index 00000000000000..b3811db6d74650 --- /dev/null +++ b/sound/soc/sof/sof-spi-dev.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +struct sof_spi_priv { + struct snd_sof_pdata *sof_pdata; + struct platform_device *pdev_pcm; +}; + +static void sof_spi_fw_cb(const struct firmware *fw, void *context) +{ + struct sof_spi_priv *priv = context; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + const struct snd_sof_machine *mach = sof_pdata->machine; + struct device *dev = sof_pdata->dev; + + sof_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", + mach->sof_fw_filename); + return; + } + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + } +} + +static const struct dev_pm_ops sof_spi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + +static int sof_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct snd_sof_machine *mach; + struct snd_sof_machine *m; + struct snd_sof_pdata *sof_pdata; + struct sof_spi_priv *priv; + int ret = 0; + + dev_dbg(&spi->dev, "SPI DSP detected"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + spi_set_drvdata(spi, priv); + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return -ENOMEM; + + /* use nocodec machine atm */ + dev_err(dev, "No matching ASoC machine driver found - using nocodec\n"); + sof_pdata->drv_name = "sof-nocodec"; + m = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + if (!m) + return -ENOMEM; + + m->drv_name = "sof-nocodec"; + m->sof_fw_filename = desc->nocodec_fw_filename; + m->sof_tplg_filename = desc->nocodec_tplg_filename; + m->ops = desc->machines[0].ops; + m->asoc_plat_name = "sof-platform"; + mach = m; + + sof_pdata->id = pci_id->device; + sof_pdata->name = spi_name(spi); + sof_pdata->machine = mach; + sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; + priv->sof_pdata = sof_pdata; + sof_pdata->spi = spi; + sof_pdata->dev = dev; + + /* register machine driver */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(sof_pdata->pdev_mach)) + return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + /* continue probing after firmware is loaded */ + ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, + dev, GFP_KERNEL, priv, sof_spi_fw_cb); + if (ret) + platform_device_unregister(sof_pdata->pdev_mach); + + return ret; +} + +static int sof_spi_remove(struct spi_device *spi) +{ + struct sof_spi_priv *priv = spi_get_drvdata(spi); + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + platform_device_unregister(sof_pdata->pdev_mach); + if (!IS_ERR_OR_NULL(priv->pdev_pcm)) + platform_device_unregister(priv->pdev_pcm); + release_firmware(sof_pdata->fw); +} + +static struct spi_driver wm8731_spi_driver = { + .driver = { + .name = "sof-spi-dev", + .of_match_table = sof_of_match, + }, + .probe = sof_spi_probe, + .remove = sof_spi_remove, +}; + +static const struct snd_sof_machine sof_spi_machines[] = { + { "INT343A", "bxt_alc298s_i2s", "intel/sof-spi.ri", + "intel/sof-spi.tplg", "0000:00:0e.0", &snd_sof_spi_ops }, +}; + +static const struct sof_dev_desc spi_desc = { + .machines = sof_spi_machines, + .nocodec_fw_filename = "intel/sof-spi.ri", + .nocodec_tplg_filename = "intel/sof-spi.tplg" +}; + +static int __init sof_spi_modinit(void) +{ + int ret; + + ret = spi_register_driver(&sof_spi_driver); + if (ret != 0) + pr_err("Failed to register SOF SPI driver: %d\n", ret); + + return ret; +} +module_init(sof_spi_modinit); + +static void __exit sof_spi_modexit(void) +{ + spi_unregister_driver(&sof_spi_driver); +} +module_exit(sof_spi_modexit); + +MODULE_LICENSE("Dual BSD/GPL"); From 4021d605afdc80fbd1911c704dee89f1f15f0a97 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:36:02 +0000 Subject: [PATCH 133/285] ASoC: SOF: Add ACPI device support Add support ACPI based SOF DSP devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-acpi-dev.c | 357 +++++++++++++++++++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 sound/soc/sof/sof-acpi-dev.c diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c new file mode 100644 index 00000000000000..dd71297e8c2558 --- /dev/null +++ b/sound/soc/sof/sof-acpi-dev.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* platform specific devices */ +#include "intel/shim.h" +#include "intel/hda.h" + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) +static struct sof_dev_desc sof_acpi_haswell_desc = { + .machines = snd_soc_acpi_intel_haswell_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = -1, + .irqindex_host_ipc = 0, + .nocodec_fw_filename = "intel/sof-hsw.ri", + .nocodec_tplg_filename = "intel/sof-hsw-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) +static struct sof_dev_desc sof_acpi_broadwell_desc = { + .machines = snd_soc_acpi_intel_broadwell_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = -1, + .irqindex_host_ipc = 0, + .nocodec_fw_filename = "intel/sof-bdw.ri", + .nocodec_tplg_filename = "intel/sof-bdw-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + +/* BYTCR uses different IRQ index */ +static struct sof_dev_desc sof_acpi_baytrailcr_desc = { + .machines = snd_soc_acpi_intel_baytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = 2, + .irqindex_host_ipc = 0, + .nocodec_fw_filename = "intel/sof-byt.ri", + .nocodec_tplg_filename = "intel/sof-byt-nocodec.tplg" +}; + +static struct sof_dev_desc sof_acpi_baytrail_desc = { + .machines = snd_soc_acpi_intel_baytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = 2, + .irqindex_host_ipc = 5, + .nocodec_fw_filename = "intel/sof-byt.ri", + .nocodec_tplg_filename = "intel/sof-byt-nocodec.tplg" +}; + +static int is_byt_cr(struct device *dev) +{ + u32 bios_status; + int status; + + if (!IS_ENABLED(CONFIG_IOSF_MBI) || !iosf_mbi_available()) { + dev_info(dev, "IOSF_MBI not enabled - can't determine CPU variant\n"); + return -EIO; + } + + status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */ + MBI_REG_READ, /* 0x10 */ + 0x006, /* BIOS_CONFIG */ + &bios_status); + + if (status) { + dev_err(dev, "error: could not read PUNIT BIOS_CONFIG\n"); + return -EIO; + } + + /* bits 26:27 mirror PMIC options */ + bios_status = (bios_status >> 26) & 3; + + if (bios_status == 1 || bios_status == 3) { + dev_info(dev, "BYT-CR detected\n"); + return 1; + } + + dev_info(dev, "BYT-CR not detected\n"); + return 0; +} + +static struct sof_dev_desc sof_acpi_cherrytrail_desc = { + .machines = snd_soc_acpi_intel_cherrytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_imr_base = 2, + .irqindex_host_ipc = 5, + .nocodec_fw_filename = "intel/sof-cht.ri", + .nocodec_tplg_filename = "intel/sof-cht-nocodec.tplg" +}; +#endif + +static struct platform_device * + mfld_new_mach_data(struct snd_sof_pdata *sof_pdata) +{ + struct snd_soc_acpi_mach pmach; + struct device *dev = &sof_pdata->pdev->dev; + const struct snd_soc_acpi_mach *mach = sof_pdata->machine; + struct platform_device *pdev = NULL; + + memset(&pmach, 0, sizeof(pmach)); + memcpy((void *)pmach.id, mach->id, ACPI_ID_LEN); + pmach.drv_name = mach->drv_name; + + pdev = platform_device_register_data(dev, mach->drv_name, -1, + &pmach, sizeof(pmach)); + return pdev; +} + +struct sof_acpi_priv { + struct snd_sof_pdata *sof_pdata; + struct platform_device *pdev_pcm; +}; + +static void sof_acpi_fw_cb(const struct firmware *fw, void *context) +{ + struct sof_acpi_priv *priv = context; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + const struct snd_soc_acpi_mach *mach = sof_pdata->machine; + struct device *dev = &sof_pdata->pdev->dev; + + sof_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", + mach->sof_fw_filename); + return; + } + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + } +} + +static const struct dev_pm_ops sof_acpi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + +static const struct sof_ops_table mach_ops[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) + {&sof_acpi_haswell_desc, &sof_hsw_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + {&sof_acpi_broadwell_desc, &sof_bdw_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + {&sof_acpi_baytrail_desc, &sof_byt_ops, mfld_new_mach_data}, + {&sof_acpi_baytrailcr_desc, &sof_byt_ops, mfld_new_mach_data}, + {&sof_acpi_cherrytrail_desc, &sof_cht_ops, mfld_new_mach_data}, +#endif +}; + +static struct snd_sof_dsp_ops * + sof_acpi_get_ops(const struct sof_dev_desc *d, + struct platform_device *(**new_mach_data) + (struct snd_sof_pdata *)) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mach_ops); i++) { + if (d == mach_ops[i].desc) { + *new_mach_data = mach_ops[i].new_data; + return mach_ops[i].ops; + } + } + + /* not found */ + return NULL; +} + +static int sof_acpi_probe(struct platform_device *pdev) +{ + const struct acpi_device_id *id; + struct device *dev = &pdev->dev; + const struct sof_dev_desc *desc; + struct snd_soc_acpi_mach *mach; + struct snd_sof_pdata *sof_pdata; + struct sof_acpi_priv *priv; + struct snd_sof_dsp_ops *ops; + struct platform_device *(*new_mach_data)(struct snd_sof_pdata *pdata); + int ret = 0; + + dev_dbg(&pdev->dev, "ACPI DSP detected"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return -ENOMEM; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + desc = (const struct sof_dev_desc *)id->driver_data; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + if (desc == &sof_acpi_baytrail_desc && is_byt_cr(dev)) + desc = &sof_acpi_baytrailcr_desc; +#endif + + /* get ops for platform */ + new_mach_data = NULL; + ops = sof_acpi_get_ops(desc, &new_mach_data); + if (!ops) { + dev_err(dev, "error: no matching ACPI descriptor ops\n"); + return -ENODEV; + } + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) + /* force nocodec mode */ + dev_warn(dev, "Force to use nocodec mode\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + if (ret < 0) + return ret; +#else + /* find machine */ + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) + /* fallback to nocodec mode */ + dev_warn(dev, "No matching ASoC machine driver found - using nocodec\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + if (ret < 0) + return ret; +#else + dev_err(dev, "No matching ASoC machine driver found - aborting probe\n"); + return -ENODEV; +#endif + } +#endif + + mach->pdata = ops; + mach->new_mach_data = (struct platform_device * + (*)(void *pdata)) new_mach_data; + + sof_pdata->machine = mach; + /* + * FIXME, this can't work for baytrail cr: + * sof_pdata->desc = (struct sof_dev_desc*) id->driver_data; + */ + sof_pdata->desc = desc; + priv->sof_pdata = sof_pdata; + sof_pdata->pdev = pdev; + dev_set_drvdata(&pdev->dev, priv); + + /* do we need to generate any machine plat data ? */ + if (mach->new_mach_data) + sof_pdata->pdev_mach = mach->new_mach_data(sof_pdata); + else + /* register machine driver, pass machine info as pdata */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + (const void *)mach, + sizeof(*mach)); + if (IS_ERR(sof_pdata->pdev_mach)) + return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + /* continue SST probing after firmware is loaded */ + dev_info(dev, "info: loading firmware %s\n", mach->sof_fw_filename); + ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, + dev, GFP_KERNEL, priv, sof_acpi_fw_cb); + if (ret) { + platform_device_unregister(sof_pdata->pdev_mach); + dev_err(dev, "error: failed to load firmware %s\n", + mach->sof_fw_filename); + } + + return ret; +} + +static void sof_acpi_shutdown(struct platform_device *pdev) +{ + snd_sof_shutdown(&pdev->dev); +} + +static int sof_acpi_remove(struct platform_device *pdev) +{ + struct sof_acpi_priv *priv = dev_get_drvdata(&pdev->dev); + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + platform_device_unregister(sof_pdata->pdev_mach); + if (!IS_ERR_OR_NULL(priv->pdev_pcm)) + platform_device_unregister(priv->pdev_pcm); + release_firmware(sof_pdata->fw); + + return 0; +} + +static const struct acpi_device_id sof_acpi_match[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) + { "INT33C8", (unsigned long)&sof_acpi_haswell_desc }, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) + { "INT3438", (unsigned long)&sof_acpi_broadwell_desc }, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + { "80860F28", (unsigned long)&sof_acpi_baytrail_desc }, + { "808622A8", (unsigned long)&sof_acpi_cherrytrail_desc }, +#endif + { } +}; +MODULE_DEVICE_TABLE(acpi, sof_acpi_match); + +/* acpi_driver definition */ +static struct platform_driver snd_sof_acpi_driver = { + .probe = sof_acpi_probe, + .remove = sof_acpi_remove, + .shutdown = sof_acpi_shutdown, + .driver = { + .name = "sof-audio-acpi", + .pm = &sof_acpi_pm, + .acpi_match_table = ACPI_PTR(sof_acpi_match), + }, +}; +module_platform_driver(snd_sof_acpi_driver); + +MODULE_LICENSE("Dual BSD/GPL"); From 7bdc920308586b628829f7002ff93abaeee3ad11 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:36:19 +0000 Subject: [PATCH 134/285] ASoC: SOF: Add PCI device support Add support for PCI based DSP devices. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-pci-dev.c | 341 ++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 sound/soc/sof/sof-pci-dev.c diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c new file mode 100644 index 00000000000000..7a2d48e0d34764 --- /dev/null +++ b/sound/soc/sof/sof-pci-dev.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof-priv.h" + +/* platform specific devices */ +#include "intel/shim.h" +#include "intel/hda.h" + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) +static struct sof_dev_desc bxt_desc = { + .machines = snd_soc_acpi_intel_bxt_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-apl.ri", + .nocodec_tplg_filename = "intel/sof-apl-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) +static struct sof_dev_desc glk_desc = { + .machines = snd_soc_acpi_intel_glk_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-glk.ri", + .nocodec_tplg_filename = "intel/sof-glk-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) +static struct snd_soc_acpi_mach sof_byt_machines[] = { + { + .id = "INT343A", + .drv_name = "edison", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt.tplg", + .asoc_plat_name = "baytrail-pcm-audio", + }, + {} +}; + +static const struct sof_dev_desc byt_desc = { + .machines = sof_byt_machines, + .resindex_lpe_base = 3, /* IRAM, but subtract IRAM offset */ + .resindex_pcicfg_base = -1, + .resindex_imr_base = 0, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-byt.ri", + .nocodec_tplg_filename = "intel/sof-byt.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) +static const struct sof_dev_desc cnl_desc = { + .machines = snd_soc_acpi_intel_cnl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-cnl.ri", + .nocodec_tplg_filename = "intel/sof-cnl.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) +static struct sof_dev_desc skl_desc = { + .machines = snd_soc_acpi_intel_skl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-skl.ri", + .nocodec_tplg_filename = "intel/sof-skl-nocodec.tplg" +}; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) +static struct sof_dev_desc kbl_desc = { + .machines = snd_soc_acpi_intel_kbl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .nocodec_fw_filename = "intel/sof-kbl.ri", + .nocodec_tplg_filename = "intel/sof-kbl-nocodec.tplg" +}; +#endif + +struct sof_pci_priv { + struct snd_sof_pdata *sof_pdata; + struct platform_device *pdev_pcm; +}; + +static void sof_pci_fw_cb(const struct firmware *fw, void *context) +{ + struct sof_pci_priv *priv = context; + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + const struct snd_soc_acpi_mach *mach = sof_pdata->machine; + struct device *dev = sof_pdata->dev; + + sof_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", + mach->sof_fw_filename); + return; + } + + /* register PCM and DAI driver */ + priv->pdev_pcm = + platform_device_register_data(dev, "sof-audio", -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(priv->pdev_pcm)) { + dev_err(dev, "Cannot register device sof-audio. Error %d\n", + (int)PTR_ERR(priv->pdev_pcm)); + } +} + +static const struct dev_pm_ops sof_pci_pm = { + SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + NULL) + .suspend_late = snd_sof_suspend_late, +}; + +static const struct sof_ops_table mach_ops[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + {&bxt_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) + {&glk_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + {&byt_desc, &sof_byt_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) + {&cnl_desc, &sof_cnl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) + {&skl_desc, &sof_apl_ops}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) + {&kbl_desc, &sof_apl_ops}, +#endif +}; + +static struct snd_sof_dsp_ops *sof_pci_get_ops(const struct sof_dev_desc *d) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mach_ops); i++) { + if (d == mach_ops[i].desc) + return mach_ops[i].ops; + } + + /* not found */ + return NULL; +} + +static int sof_pci_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct device *dev = &pci->dev; + const struct sof_dev_desc *desc = + (const struct sof_dev_desc *)pci_id->driver_data; + struct snd_soc_acpi_mach *mach; + struct snd_sof_pdata *sof_pdata; + struct sof_pci_priv *priv; + struct snd_sof_dsp_ops *ops; + int ret = 0; + + dev_dbg(&pci->dev, "PCI DSP detected"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + pci_set_drvdata(pci, priv); + + sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); + if (!sof_pdata) + return -ENOMEM; + + ret = pci_enable_device(pci); + if (ret < 0) + return ret; + + ret = pci_request_regions(pci, "Audio DSP"); + if (ret < 0) + return ret; + + /* get ops for platform */ + ops = sof_pci_get_ops(desc); + if (!ops) { + dev_err(dev, "error: no matching PCI descriptor ops\n"); + return -ENODEV; + } + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) + /* force nocodec mode */ + dev_warn(dev, "Force to use nocodec mode\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + if (ret < 0) + return ret; +#else + /* find machine */ + mach = snd_soc_acpi_find_machine(desc->machines); + if (!mach) { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) + /* fallback to nocodec mode */ + dev_warn(dev, "No matching ASoC machine driver found - using nocodec\n"); + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + if (ret < 0) + return ret; +#else + dev_err(dev, "No matching ASoC machine driver found - aborting probe\n"); + return -ENODEV; +#endif + } +#endif + + mach->pdata = ops; + + sof_pdata->id = pci_id->device; + sof_pdata->name = pci_name(pci); + sof_pdata->machine = mach; + sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; + priv->sof_pdata = sof_pdata; + sof_pdata->pci = pci; + sof_pdata->dev = &pci->dev; + + /* register machine driver */ + sof_pdata->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + sof_pdata, sizeof(*sof_pdata)); + if (IS_ERR(sof_pdata->pdev_mach)) + return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", + dev_name(&sof_pdata->pdev_mach->dev)); + + /* continue probing after firmware is loaded */ + dev_info(dev, "info: loading firmware %s\n", mach->sof_fw_filename); + ret = request_firmware_nowait(THIS_MODULE, true, mach->sof_fw_filename, + dev, GFP_KERNEL, priv, sof_pci_fw_cb); + if (ret) { + platform_device_unregister(sof_pdata->pdev_mach); + dev_err(dev, "error: failed to load firmware %s\n", + mach->sof_fw_filename); + } + + return ret; +} + +static void sof_pci_shutdown(struct pci_dev *pci) +{ + snd_sof_shutdown(&pci->dev); +} + +static void sof_pci_remove(struct pci_dev *pci) +{ + struct sof_pci_priv *priv = pci_get_drvdata(pci); + struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + + platform_device_unregister(sof_pdata->pdev_mach); + if (!IS_ERR_OR_NULL(priv->pdev_pcm)) + platform_device_unregister(priv->pdev_pcm); + release_firmware(sof_pdata->fw); + pci_release_regions(pci); +} + +/* PCI IDs */ +static const struct pci_device_id sof_pci_ids[] = { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + /* BXT-P & Apollolake */ + { PCI_DEVICE(0x8086, 0x5a98), + .driver_data = (unsigned long)&bxt_desc}, + { PCI_DEVICE(0x8086, 0x1a98), + .driver_data = (unsigned long)&bxt_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) + { PCI_DEVICE(0x8086, 0x119a), + .driver_data = (unsigned long)&byt_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) + { PCI_DEVICE(0x8086, 0x9dc8), + .driver_data = (unsigned long)&cnl_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) + { PCI_DEVICE(0x8086, 0x9d71), + .driver_data = (unsigned long)&kbl_desc}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) + { PCI_DEVICE(0x8086, 0x9d70), + .driver_data = (unsigned long)&skl_desc}, +#endif + { 0, } +}; +MODULE_DEVICE_TABLE(pci, sof_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_driver = { + .name = "sof-audio-pci", + .id_table = sof_pci_ids, + .probe = sof_pci_probe, + .remove = sof_pci_remove, + .shutdown = sof_pci_shutdown, + .driver = { + .pm = &sof_pci_pm, + }, +}; +module_pci_driver(snd_sof_pci_driver); + +MODULE_LICENSE("Dual BSD/GPL"); From bea036a44ca72bb8c69f8c94327edee4cad3b08a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 8 Jan 2018 20:40:02 +0000 Subject: [PATCH 135/285] ASoC: SOF: Add Build support for SOF core and Intel drivers Build SOF core and Intel-specific drivers. Signed-off-by: Liam Girdwood --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/sof/Kconfig | 44 +++++++++++++++++ sound/soc/sof/Makefile | 22 +++++++++ sound/soc/sof/intel/Kconfig | 94 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/Makefile | 15 ++++++ 6 files changed, 177 insertions(+) create mode 100644 sound/soc/sof/Kconfig create mode 100644 sound/soc/sof/Makefile create mode 100644 sound/soc/sof/intel/Kconfig create mode 100644 sound/soc/sof/intel/Makefile diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 41af6b9cc3500b..2c0e6e5c7d7eb8 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -64,6 +64,7 @@ source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" +source "sound/soc/sof/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 8d92492183d28c..916aee8c5a8961 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ +obj-$(CONFIG_SND_SOC) += sof/ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += stm/ diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig new file mode 100644 index 00000000000000..0b2e7a5fd31e81 --- /dev/null +++ b/sound/soc/sof/Kconfig @@ -0,0 +1,44 @@ +config SND_SOC_SOF_PCI + tristate + +config SND_SOC_SOF_ACPI + tristate + +config SND_SOC_SOF_PLATFORM + tristate + + +config SND_SOC_SOF + tristate "Sound Open Firmware Support" + default m + select SND_SOC_TOPOLOGY + select SND_SOC_COMPRESS + help + This adds support for SOF + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_NOCODEC + tristate "SOF nocodec mode Support" + depends on SND_SOC_SOF + help + This adds support for a dummy/nocodec machine driver fallback + option if no known codec is detected. This is typically only + enabled for developers or devices where the sound card is + controlled externally + Say Y if you need this nocodec fallback option + If unsure select "N". + +config SND_SOC_SOF_FORCE_NOCODEC_MODE + tristate "SOF force nocodec Mode" + depends on SND_SOC_SOF_NOCODEC + help + This forces SOF to use dummy/nocodec as machine driver, even + though there is a codec detected on the real platform. This is + typically only enabled for developers for debug purposes, before + codec/machine driver is ready, or to exclude the impact of those + drivers + Say Y if you need this force nocodec mode option + If unsure select "N". + +source "sound/soc/sof/intel/Kconfig" diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile new file mode 100644 index 00000000000000..ece93000a1974d --- /dev/null +++ b/sound/soc/sof/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ + control.o trace.o compressed.o +snd-sof-spi-objs := hw-spi.o + +snd-sof-pci-objs := sof-pci-dev.o +snd-sof-acpi-objs := sof-acpi-dev.o +snd-sof-platform-objs := sof-platform-dev.o +snd-sof-nocodec-objs := nocodec.o + +obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o +obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o + +obj-$(CONFIG_SND_SOC_SOF_ACPI) += sof-acpi-dev.o +obj-$(CONFIG_SND_SOC_SOF_PCI) += sof-pci-dev.o +obj-$(CONFIG_SND_SOC_SOF_SPI) += sof-spi-dev.o +obj-$(SND_SOC_SOF_PLATFORM) += sof-platform-dev.o + +obj-$(CONFIG_SND_SOC_SOF_SPIDSP) += snd-sof-spi.o + +obj-$(CONFIG_SND_SOC_SOF_INTEL) += intel/ diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig new file mode 100644 index 00000000000000..2780750d6289d7 --- /dev/null +++ b/sound/soc/sof/intel/Kconfig @@ -0,0 +1,94 @@ +config SND_SOC_SOF_INTEL + tristate "SOF support for Intel audio DSPs" + depends on SND_SOC_SOF + depends on SND_DMA_SGBUF + select SND_SOC_INTEL_MACH + help + This adds support for Sound Open Firmware for Intel(R) platforms. + Say Y if you have such a device. + If unsure select "N". + +if SND_SOC_SOF_INTEL + +config SND_SOC_SOF_BAYTRAIL + tristate "SOF support for Baytrail, Braswell and Cherrytrail" + select SND_SOC_SOF_ACPI + select SND_SOC_ACPI_INTEL_MATCH + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Baytrail, Braswell or Cherrytrail processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_HASWELL + tristate "SOF support for Haswell" + select SND_SOC_SOF_ACPI + select SND_SOC_ACPI_INTEL_MATCH + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Haswell processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_BROADWELL + tristate "SOF support for Broadwell" + depends on SND_SOC_SOF_INTEL + select SND_SOC_SOF_ACPI + select SND_SOC_ACPI_INTEL_MATCH + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Broadwell processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_APOLLOLAKE + tristate "SOF support for Apollolake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Apollolake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_GEMINILAKE + tristate "SOF support for GeminiLake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Geminilake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_CANNONLAKE + tristate "SOF support for Cannonlake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Cannonlake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_KABYLAKE + tristate "SOF support for Kabylake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Kabylake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_SKYLAKE + tristate "SOF support for Skylake" + select SND_SOC_SOF_HDA_COMMON + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Skylake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_HDA_COMMON + tristate + select SND_SOC_SOF_PCI + select SND_SOC_ACPI_INTEL_MATCH + +endif ## SND_SOC_SOF_INTEL diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile new file mode 100644 index 00000000000000..dfeb4fcf217b90 --- /dev/null +++ b/sound/soc/sof/intel/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +ccflags-y += -DDEBUG + +snd-sof-intel-byt-objs := byt.o +snd-sof-intel-hsw-objs := hsw.o +snd-sof-intel-bdw-objs := bdw.o +snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ + hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ + skl.o apl.o cnl.o + +obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-intel-byt.o +obj-$(CONFIG_SND_SOC_SOF_HASWELL) += snd-sof-intel-hsw.o +obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-intel-bdw.o +obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o From 2c48caba707c5e1c8bc6083d0f02b616c688f4fe Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 31 May 2018 17:35:22 -0500 Subject: [PATCH 136/285] ASoC: Intel: Kconfig: disable SST and legacy drivers when SOF is selected Make sure distros don't run into issues with multiple incompatible drivers selected for the same hardware. Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 0caa1f4eb94d7d..08f00058700023 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -61,7 +61,7 @@ config SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n + depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n && SND_SOC_SOF_BAYTRAIL=n select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE @@ -91,7 +91,7 @@ config SND_SST_ATOM_HIFI2_PLATFORM_PCI config SND_SST_ATOM_HIFI2_PLATFORM_ACPI tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms" default ACPI - depends on X86 && ACPI + depends on X86 && ACPI && SND_SOC_SOF_BAYTRAIL=n select SND_SST_IPC_ACPI select SND_SST_ATOM_HIFI2_PLATFORM select SND_SOC_ACPI_INTEL_MATCH From 5119fdb26647c81b736cd760dccc8aa596d819e9 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 25 May 2018 16:29:46 +0800 Subject: [PATCH 137/285] ASoC: SOF: refine dapm route loading in sof Some virtual routes and widgets may been added in topology to reusing machine driver in Linux kernel. For virtual routes, both sink and source are not buffer. Since only buffer linked to component is supported by FW, others are reported as error, add check in route function, do not send it to FW when both source and sink are not buffer Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/topology.c | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 5fe74fa41e423f..b98078fe49987c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1600,7 +1600,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_sof_widget *source_swidget, *sink_swidget; struct snd_sof_pcm *spcm; struct sof_ipc_reply reply; - int ret; + int ret = 0; memset(&connect, 0, sizeof(connect)); connect.hdr.size = sizeof(connect); @@ -1640,24 +1640,39 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, connect.sink_id = sink_swidget->comp_id; - ret = sof_ipc_tx_message(sdev->ipc, - connect.hdr.cmd, &connect, sizeof(connect), - &reply, sizeof(reply)); - - /* check IPC return value */ - if (ret < 0) { - dev_err(sdev->dev, "error: failed to add route sink %s control %s source %s\n", - route->sink, route->control ? route->control : "none", - route->source); - return ret; - } + /* Some virtual routes and widgets may been added in topology for + * compatibility. For virtual routes, both sink and source are not + * buffer. Since only buffer linked to component is supported by + * FW, others are reported as error, add check in route function, + * do not send it to FW when both source and sink are not buffer + */ + if (source_swidget->id != snd_soc_dapm_buffer && + sink_swidget->id != snd_soc_dapm_buffer) { + dev_dbg(sdev->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n", + route->source, route->sink); + } else { + ret = sof_ipc_tx_message(sdev->ipc, + connect.hdr.cmd, + &connect, sizeof(connect), + &reply, sizeof(reply)); + + /* check IPC return value */ + if (ret < 0) { + dev_err(sdev->dev, "error: failed to add route sink %s control %s source %s\n", + route->sink, + route->control ? route->control : "none", + route->source); + return ret; + } - /* check IPC reply */ - if (reply.error < 0) { - dev_err(sdev->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", - route->sink, route->control ? route->control : "none", - route->source, reply.error); - //return ret; // TODO: + /* check IPC reply */ + if (reply.error < 0) { + dev_err(sdev->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", + route->sink, + route->control ? route->control : "none", + route->source, reply.error); + return reply.error; + } } return ret; From 92e8a5070497bd2ef11633b89adbb5bda31584b8 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Fri, 25 May 2018 13:34:43 +0800 Subject: [PATCH 138/285] ASoC: SOF: parse Xtensa exception causes Xtensa DSP panic will have a EXCCAUSE contains the exception cause. Parse the EXCCAUSE into readable message from Xtensa Instruction Set Architecture (ISA) Reference Manual Signed-off-by: Pan Xiuli --- sound/soc/sof/core.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index e15c393cd57476..9e0e34075f4f07 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -140,12 +140,86 @@ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_IDLE, "can't enter idle"}, }; +struct sof_exception_cause { + u32 id; + const char *msg; + const char *description; +}; + +/* From 4.4.1.5 table 4-64 Exception Causes of + * Xtensa Instruction Set Architecture (ISA) Reference Manual + */ +static const struct sof_exception_cause xtensa_exception_causes[] = { + {0, "IllegalInstructionCause", "Illegal instruction"}, + {1, "SyscallCause", "SYSCALL instruction"}, + {2, "InstructionFetchErrorCause", + "Processor internal physical address or data error during instruction fetch"}, + {3, "LoadStoreErrorCause", + "Processor internal physical address or data error during load or store"}, + {4, "Level1InterruptCause", + "Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register"}, + {5, "AllocaCause", + "MOVSP instruction, if caller’s registers are not in the register file"}, + {6, "IntegerDivideByZeroCause", + "QUOS, QUOU, REMS, or REMU divisor operand is zero"}, + {8, "PrivilegedCause", + "Attempt to execute a privileged operation when CRING ? 0"}, + {9, "LoadStoreAlignmentCause", "Load or store to an unaligned address"}, + {12, "InstrPIFDataErrorCause", + "PIF data error during instruction fetch"}, + {13, "LoadStorePIFDataErrorCause", + "Synchronous PIF data error during LoadStore access"}, + {14, "InstrPIFAddrErrorCause", + "PIF address error during instruction fetch"}, + {15, "LoadStorePIFAddrErrorCause", + "Synchronous PIF address error during LoadStore access"}, + {16, "InstTLBMissCause", "Error during Instruction TLB refill"}, + {17, "InstTLBMultiHitCause", + "Multiple instruction TLB entries matched"}, + {18, "InstFetchPrivilegeCause", + "An instruction fetch referenced a virtual address at a ring level less than CRING"}, + {20, "InstFetchProhibitedCause", + "An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch"}, + {24, "LoadStoreTLBMissCause", + "Error during TLB refill for a load or store"}, + {25, "LoadStoreTLBMultiHitCause", + "Multiple TLB entries matched for a load or store"}, + {26, "LoadStorePrivilegeCause", + "A load or store referenced a virtual address at a ring level less than CRING"}, + {28, "LoadProhibitedCause", + "A load referenced a page mapped with an attribute that does not permit loads"}, + {32, "Coprocessor0Disabled", + "Coprocessor 0 instruction when cp0 disabled"}, + {33, "Coprocessor1Disabled", + "Coprocessor 1 instruction when cp1 disabled"}, + {34, "Coprocessor2Disabled", + "Coprocessor 2 instruction when cp2 disabled"}, + {35, "Coprocessor3Disabled", + "Coprocessor 3 instruction when cp3 disabled"}, + {36, "Coprocessor4Disabled", + "Coprocessor 4 instruction when cp4 disabled"}, + {37, "Coprocessor5Disabled", + "Coprocessor 5 instruction when cp5 disabled"}, + {38, "Coprocessor6Disabled", + "Coprocessor 6 instruction when cp6 disabled"}, + {39, "Coprocessor7Disabled", + "Coprocessor 7 instruction when cp7 disabled"}, +}; + /* only need xtensa atm */ static void sof_arch_dsp_oops(struct snd_sof_dev *sdev, void *oops) { struct sof_ipc_dsp_oops_xtensa *xoops = oops; + int i; dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { + if (xtensa_exception_causes[i].id == xoops->exccause) { + dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", + xtensa_exception_causes[i].msg, + xtensa_exception_causes[i].description); + } + } dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", From 507d6f9bbcfe98678d3b8c5577a0f04866f6a654 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 26 May 2018 23:29:09 -0700 Subject: [PATCH 139/285] ASoC: uapi: sof: Add ipc config params and topology tokens for DMIC DAI type This patch adds the ipc config params and the topology tokens for SOF DMIC DAI type. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-ipc.h | 25 +++++++++++++++++++++++-- include/uapi/sound/sof-topology.h | 19 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 9b33691ac8a94b..d1c972abd03357 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -243,10 +243,31 @@ struct sof_ipc_dai_hda_params { /* TODO */ } __attribute__((packed)); +/* PDM controller configuration parameters */ +struct sof_ipc_dai_dmic_pdm_ctrl { + uint16_t id; /* pdm controller id */ + uint16_t enable_mic_a; /* Use A (left) channel mic (0 or 1)*/ + uint16_t enable_mic_b; /* Use B (right) channel mic (0 or 1)*/ + uint16_t polarity_mic_a; /* Optionally invert mic A signal (0 or 1) */ + uint16_t polarity_mic_b; /* Optionally invert mic B signal (0 or 1) */ + uint16_t clk_edge; /* Optionally swap data clock edge (0 or 1) */ + uint16_t skew; /* Adjust PDM data sampling vs. clock (0..15) */ +} __attribute__((packed)); + /* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */ struct sof_ipc_dai_dmic_params { - struct sof_ipc_hdr hdr; - /* TODO */ + uint32_t driver_ipc_version; + uint32_t pdmclk_min; /* Minimum microphone clock in Hz (100000..N) */ + uint32_t pdmclk_max; /* Maximum microphone clock in Hz (min...N) */ + uint32_t fifo_fs_a; /* FIFO A sample rate in Hz (8000..96000) */ + uint32_t fifo_fs_b; /* FIFO B sample rate in Hz (8000..96000) */ + uint16_t fifo_bits_a; /* FIFO A word length (16 or 24) */ + uint16_t fifo_bits_b; /* FIFO B word length (16 or 24) */ + uint16_t duty_min; /* Min. mic clock duty cycle in % (20..80) */ + uint16_t duty_max; /* Max. mic clock duty cycle in % (min..80) */ + uint32_t num_pdm_active; /* Number of active pdm controllers */ + /* variable number of pdm controller config */ + struct sof_ipc_dai_dmic_pdm_ctrl pdm[0]; } __attribute__((packed)); /* general purpose DAI configuration */ diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 62c3f242509701..5941c4cb8623a3 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -73,4 +73,23 @@ #define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 #define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 +/* DMIC */ +#define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 +#define SOF_TKN_INTEL_DMIC_CLK_MIN 601 +#define SOF_TKN_INTEL_DMIC_CLK_MAX 602 +#define SOF_TKN_INTEL_DMIC_DUTY_MIN 603 +#define SOF_TKN_INTEL_DMIC_DUTY_MAX 604 +#define SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE 605 +#define SOF_TKN_INTEL_DMIC_SAMPLE_RATE 608 +#define SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH 609 + +/* DMIC PDM */ +#define SOF_TKN_INTEL_DMIC_PDM_CTRL_ID 700 +#define SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable 701 +#define SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable 702 +#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_A 703 +#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_B 704 +#define SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE 705 +#define SOF_TKN_INTEL_DMIC_PDM_SKEW 706 + #endif From 49427039740abef976f5a0a89784f1009febc420 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 26 May 2018 23:29:10 -0700 Subject: [PATCH 140/285] ASoC: SOF: Add support for parsing DMIC specific tokens from topology Update dmic specific tokens in topology parser and configure ipc params for DMIC DAI type. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 231 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 219 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index b98078fe49987c..a2c986e4cad094 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -285,6 +285,15 @@ static int get_token_u32(void *elem, void *object, u32 offset, u32 size) return 0; } +static int get_token_u16(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + u16 *val = object + offset; + + *val = velem->value; + return 0; +} + static int get_token_comp_format(void *elem, void *object, u32 offset, u32 size) { struct snd_soc_tplg_vendor_string_elem *velem = elem; @@ -414,6 +423,69 @@ static const struct sof_topology_token ssp_tokens[] = { /* DMIC */ static const struct sof_topology_token dmic_tokens[] = { + {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version), + 0}, + {SOF_TKN_INTEL_DMIC_CLK_MIN, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min), 0}, + {SOF_TKN_INTEL_DMIC_CLK_MAX, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max), 0}, + {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, fifo_fs_a), 0}, + {SOF_TKN_INTEL_DMIC_DUTY_MIN, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_min), 0}, + {SOF_TKN_INTEL_DMIC_DUTY_MAX, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_max), 0}, + {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, + num_pdm_active), 0}, + {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, fifo_bits_a), 0}, +}; + +/* + * DMIC PDM Tokens + * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token + * as it increments the index while parsing the array of pdm tokens + * and determines the correct offset + */ +static const struct sof_topology_token dmic_pdm_tokens[] = { + {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, + {SOF_TKN_INTEL_DMIC_PDM_SKEW, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + 0}, }; /* HDA */ @@ -480,6 +552,65 @@ static void sof_parse_string_tokens(struct snd_soc_component *scomp, } } +/* helper function to parse DMIC pdm specific tokens */ +static void sof_parse_pdm_tokens(struct snd_soc_component *scomp, + struct snd_soc_tplg_vendor_value_elem *elem, + void *object, + struct sof_topology_token token) +{ + size_t size = sizeof(struct sof_ipc_dai_dmic_pdm_ctrl); + u32 offset = token.offset; + /* pdm_index determines the index in pdm[] to populate the token */ + static int pdm_index; + /* determines the offset of the item in sof_ipc_dai_dmic_pdm_ctrl */ + u32 pdm_offset; + + switch (token.token) { + case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID: + token.get_token(elem, object, offset + pdm_index * size, + token.size); + /* increment the pdm_index */ + pdm_index++; + return; + case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + enable_mic_a); + break; + case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + enable_mic_b); + break; + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + polarity_mic_a); + break; + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + polarity_mic_b); + break; + case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + clk_edge); + break; + case SOF_TKN_INTEL_DMIC_PDM_SKEW: + pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, + skew); + break; + default: + break; + } + + /* + * offset: address pointing to pdm[0] in dmic_params + * pdm_index - 1: index of the array item to be populated + * pdm_offset: item offset in sof_ipc_dai_dmic_pdm_ctrl + * indexed by pdm_index + */ + token.get_token(elem, object, + offset + (pdm_index - 1) * size + pdm_offset, + token.size); +} + static void sof_parse_word_tokens(struct snd_soc_component *scomp, void *object, const struct sof_topology_token *tokens, @@ -496,7 +627,8 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, /* search for token */ for (j = 0; j < count; j++) { /* match token type */ - if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_WORD) + if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT)) continue; /* match token id */ @@ -504,8 +636,24 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, continue; /* matched - now load token */ - tokens[j].get_token(elem, object, tokens[j].offset, - tokens[j].size); + switch (tokens[j].token) { + case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID: + case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: + case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: + case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B: + case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE: + case SOF_TKN_INTEL_DMIC_PDM_SKEW: + /* parse pdm specific tokens */ + sof_parse_pdm_tokens(scomp, elem, object, + tokens[j]); + break; + default: + tokens[j].get_token(elem, object, + tokens[j].offset, + tokens[j].size); + break; + } } } } @@ -1400,16 +1548,15 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_dai_config *ipc_config; struct sof_ipc_reply reply; - u32 size = sizeof(*config); - int ret; + u32 size; + int ret, j; - /* init IPC */ memset(&config->dmic, 0, sizeof(struct sof_ipc_dai_dmic_params)); - config->hdr.size = size; - /* get any bespoke DAI tokens */ - ret = sof_parse_tokens(scomp, config, dmic_tokens, + /* get DMIC tokens */ + ret = sof_parse_tokens(scomp, &config->dmic, dmic_tokens, ARRAY_SIZE(dmic_tokens), private->array, private->size); if (ret != 0) { @@ -1418,18 +1565,78 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, return ret; } - dev_dbg(sdev->dev, "tplg: config DMIC%d fmt 0x%x\n", - config->id, config->format); + /* + * allocate memory for common dai params, dmic params + * and dmic pdm controller params + */ + ipc_config = kzalloc(sizeof(*config) + + sizeof(struct sof_ipc_dai_dmic_pdm_ctrl) * + config->dmic.num_pdm_active, + GFP_KERNEL); + if (!ipc_config) { + dev_err(sdev->dev, "error: allocating memory for config\n"); + return -ENOMEM; + } + + /* copy the common dai config and dmic params */ + memcpy(ipc_config, config, sizeof(*config)); + + /* get DMIC PDM tokens */ + ret = sof_parse_tokens(scomp, &ipc_config->dmic, dmic_pdm_tokens, + ARRAY_SIZE(dmic_pdm_tokens), private->array, + private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse dmic pdm tokens failed %d\n", + private->size); + kfree(ipc_config); + return ret; + } + + /* set IPC header size */ + size = sizeof(*ipc_config); + ipc_config->hdr.size = size; + + /* debug messages */ + dev_dbg(sdev->dev, "tplg: config DMIC%d driver version %d\n", + ipc_config->id, ipc_config->dmic.driver_ipc_version); + dev_dbg(sdev->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n", + ipc_config->dmic.pdmclk_min, ipc_config->dmic.pdmclk_max, + ipc_config->dmic.duty_min); + dev_dbg(sdev->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n", + ipc_config->dmic.duty_max, ipc_config->dmic.fifo_fs_a, + ipc_config->dmic.num_pdm_active); + dev_dbg(sdev->dev, "fifo word length %hd\n", + ipc_config->dmic.fifo_bits_a); + + for (j = 0; j < ipc_config->dmic.num_pdm_active; j++) { + dev_dbg(sdev->dev, "pdm %hd mic a %hd mic b %hd\n", + ipc_config->dmic.pdm[j].id, + ipc_config->dmic.pdm[j].enable_mic_a, + ipc_config->dmic.pdm[j].enable_mic_b); + dev_dbg(sdev->dev, "pdm %hd polarity a %hd polarity b %hd\n", + ipc_config->dmic.pdm[j].id, + ipc_config->dmic.pdm[j].polarity_mic_a, + ipc_config->dmic.pdm[j].polarity_mic_b); + dev_dbg(sdev->dev, "pdm %hd clk_edge %hd skew %hd\n", + ipc_config->dmic.pdm[j].id, + ipc_config->dmic.pdm[j].clk_edge, + ipc_config->dmic.pdm[j].skew); + } + + /* TODO: check if fifo_b word length is needed */ + ipc_config->dmic.fifo_bits_b = ipc_config->dmic.fifo_bits_a; /* send message to DSP */ ret = sof_ipc_tx_message(sdev->ipc, - config->hdr.cmd, config, size, &reply, + ipc_config->hdr.cmd, ipc_config, size, &reply, sizeof(reply)); if (ret < 0) dev_err(sdev->dev, "error: failed to set DAI config for DMIC%d\n", config->id); + kfree(ipc_config); + return ret; } From 3f3abba33457f77940faf902178122b71f1f20dc Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 26 May 2018 23:29:11 -0700 Subject: [PATCH 141/285] ASoC: SOF: support DMIC DAI type during link fixup Handle DMIC configuration errors during link fixup. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pcm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 32d114ee748f7e..50a95fdaae29aa 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -548,7 +548,15 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, break; case SOF_DAI_INTEL_DMIC: - /* fallthrough */ + /* DMIC only supports 16 or 32 bit formats */ + if (dai->comp_dai.config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { + dev_err(sdev->dev, + "error: invalid fmt %d for DAI type %d\n", + dai->comp_dai.config.frame_fmt, + dai->dai_config.type); + } + /* TODO: add any other DMIC specific fixups */ + break; case SOF_DAI_INTEL_HDA: /* fallthrough */ default: From f1ebcc29c3d8e3e1c823c796c6c4598453153fa5 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 15 May 2018 23:27:28 +0800 Subject: [PATCH 142/285] [WORKAROUND] ASoC: SOF: start HDA DMA at hw_params() stage and remove stream_trigger() It require HDA DMA to be available before trigger_start(), Here move host side DMA start to earlier stage(hw_params) and remove stream_trigger() as it is no need anymore. This fix will help to remove HDA DMA workaround fix inside firmware. TODO: revisit this patch for SOF 1.2, this removes the ability to use the RUN bits for multi-stream synchronization and is likely not an acceptable long-term solution Signed-off-by: Keyon Jie Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/apl.c | 1 - sound/soc/sof/intel/cnl.c | 1 - sound/soc/sof/intel/hda-pcm.c | 23 +++++++++++++++-------- sound/soc/sof/intel/skl.c | 1 - sound/soc/sof/pcm.c | 4 ---- sound/soc/sof/sof-priv.h | 5 ----- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index d36e30d2a8b388..98274b02943cf4 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -80,7 +80,6 @@ struct snd_sof_dsp_ops sof_apl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, - .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 80d6cc772fd74a..8f9ad191f0ce60 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -208,7 +208,6 @@ struct snd_sof_dsp_ops sof_cnl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, - .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 0abb2e5778cae2..1b50d87bd6bed4 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -123,15 +123,22 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, /* disable SPIB, to enable buffer wrap for stream */ hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); - return stream->tag; -} - -int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, int cmd) -{ - struct sof_intel_hda_stream *stream = substream->runtime->private_data; + /* + * start HDA DMA here, as DSP require the DMA copy is available + * at its trigger start, which is actually before stream trigger + * start + */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + 1 << stream->index, + 1 << stream->index); + + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK, + SOF_HDA_SD_CTL_DMA_START | + SOF_HDA_CL_DMA_SD_INT_MASK); - return hda_dsp_stream_trigger(sdev, stream, cmd); + return stream->tag; } int hda_dsp_pcm_open(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index d4b6de626456b1..671f452e9b16d4 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -85,7 +85,6 @@ struct snd_sof_dsp_ops sof_skl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, - .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 50a95fdaae29aa..32ca808eb8624b 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -197,7 +197,6 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_sof_pcm *spcm = rtd->sof; struct sof_ipc_stream stream; struct sof_ipc_reply reply; - const struct snd_sof_dsp_ops *ops = sdev->ops; int ret = 0; /* nothing todo for BE */ @@ -236,9 +235,6 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, sizeof(stream), &reply, sizeof(reply)); - if (ops && ops->host_stream_trigger) - ret = ops->host_stream_trigger(sdev, substream, cmd); - return ret; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index bde985f6f438be..7e86faef5aa287 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -116,11 +116,6 @@ struct snd_sof_dsp_ops { struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); - /* host stream trigger */ - int (*host_stream_trigger)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - int cmd); - /* FW loading */ int (*load_firmware)(struct snd_sof_dev *sof_dev, const struct firmware *fw); From 0ba34ad6a4da54a1baf13def2e81bf8a4eec6a3c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 8 Mar 2018 18:36:30 -0600 Subject: [PATCH 143/285] ASoC: Intel: make bytcht_da7213 with SOF Disable hard-coded routes when SOF is enabled Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bytcht_da7213.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index 2179dedb28ad6d..1e90a9c4b1e892 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -56,6 +56,7 @@ static const struct snd_soc_dapm_route audio_map[] = { {"MIC1", NULL, "Headset Mic"}, {"MIC2", NULL, "Mic"}, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* SOC-codec link */ {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, @@ -64,6 +65,7 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Playback", NULL, "ssp2 Tx"}, {"ssp2 Rx", NULL, "Capture"}, +#endif }; static int codec_fixup(struct snd_soc_pcm_runtime *rtd, From 8b2b17c610264b12da260747ba5ac8b584124e51 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 8 Mar 2018 18:39:33 -0600 Subject: [PATCH 144/285] ASoC: Intel: make cht_bsw_max98090 work with SOF Disable hard-coded routes when SOF is enabled Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_max98090_ti.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index d3e1c7e12004dd..bbb281f64cbdd3 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -88,12 +88,14 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"Headphone", NULL, "HPR"}, {"Ext Spk", NULL, "SPKL"}, {"Ext Spk", NULL, "SPKR"}, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"HiFi Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "HiFi Capture"}, +#endif {"Headphone", NULL, "Platform Clock"}, {"Headset Mic", NULL, "Platform Clock"}, {"Int Mic", NULL, "Platform Clock"}, From 46a119903fd02e73a4f065f3a8a1d16e548eda19 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 10 Nov 2017 14:34:32 -0600 Subject: [PATCH 145/285] ASoC: Intel: cht-bsw-rt5645: work with SOF Disable hard-coded routes when SOF is selected TODO: bytcr is not yet supported Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_rt5645.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 49ba1a956a06a0..74b30304a2615b 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -160,35 +160,43 @@ static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = { }; static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "AIF1 Capture"}, +#endif }; static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF2 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "AIF2 Capture"}, +#endif }; static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF1 Playback", NULL, "ssp0 Tx"}, {"ssp0 Tx", NULL, "modem_out"}, {"modem_in", NULL, "ssp0 Rx" }, {"ssp0 Rx", NULL, "AIF1 Capture"}, +#endif }; static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF2 Playback", NULL, "ssp0 Tx"}, {"ssp0 Tx", NULL, "modem_out"}, {"modem_in", NULL, "ssp0 Rx" }, {"ssp0 Rx", NULL, "AIF2 Capture"}, +#endif }; static const struct snd_kcontrol_new cht_mc_controls[] = { @@ -585,11 +593,13 @@ static int snd_cht_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (is_valleyview()) { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* FIXME: bytcr not supported yet */ struct sst_platform_info *p_info = mach->pdata; const struct sst_res_info *res_info = p_info->res_info; if (res_info->acpi_ipc_irq_index == 0) is_bytcr = true; +#endif } if (is_bytcr) { From 2759eb0a85898d2b58510d9e0257af585fcdf608 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 29 Nov 2017 19:14:19 -0600 Subject: [PATCH 146/285] ASoC: Intel: cht-bsw-rt5672: work with SOF Disable hard-coded routes when SOF is enabled Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_rt5672.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index e5aa13058dd738..3a2754676f3f1c 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -127,12 +127,14 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"Ext Spk", NULL, "SPOLN"}, {"Ext Spk", NULL, "SPORP"}, {"Ext Spk", NULL, "SPORN"}, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx"}, {"codec_in1", NULL, "ssp2 Rx"}, {"ssp2 Rx", NULL, "AIF1 Capture"}, +#endif {"Headphone", NULL, "Platform Clock"}, {"Headset Mic", NULL, "Platform Clock"}, {"Int Mic", NULL, "Platform Clock"}, From 87d8985bfa9355d2fe9afb1a146583a541d7b6a3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 20 Dec 2017 18:36:49 -0600 Subject: [PATCH 147/285] ASoC: Intel: make bytcr_rt5640 work with SOF Disable hard-coded routes when SOF is enabled TODO: bytcr is not yet supported Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bytcr_rt5640.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a8d8bff788e70b..8bdf820903541a 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -489,6 +489,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) { ret = snd_soc_dapm_add_routes(&card->dapm, byt_rt5640_ssp2_aif2_map, @@ -508,6 +509,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) } if (ret) return ret; +#endif if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) { ret = snd_soc_dapm_add_routes(&card->dapm, @@ -775,11 +777,13 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (is_valleyview()) { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* FIXME: bytcr not supported yet */ struct sst_platform_info *p_info = mach->pdata; const struct sst_res_info *res_info = p_info->res_info; if (res_info->acpi_ipc_irq_index == 0) is_bytcr = true; +#endif } if (is_bytcr) { From 9b9e3920a89e3b4be77407ffdc4db23c389dacc9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 7 Dec 2017 21:48:51 +0000 Subject: [PATCH 148/285] ASoC: Intel: Make sure HSW/BDW based machine drivers build for SOF HSW/BDW use hard coded IPC calls to set SSP, not needed in SOF as SSP is configured via topology. Signed-off-by: Liam Girdwood --- sound/soc/intel/boards/bdw-rt5677.c | 4 ++++ sound/soc/intel/boards/broadwell.c | 4 ++++ sound/soc/intel/boards/haswell.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 6ea360f33575ae..f287f192929ca5 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -181,6 +181,7 @@ static const struct snd_soc_ops bdw_rt5677_ops = { .hw_params = bdw_rt5677_hw_params, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); @@ -199,6 +200,7 @@ static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } +#endif static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) { @@ -266,7 +268,9 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .init = bdw_rt5677_rtd_init, +#endif .trigger = { SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 7b0ee67b4fc8b0..776a1fa7a1e1d6 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -130,6 +130,7 @@ static const struct snd_soc_ops broadwell_rt286_ops = { .hw_params = broadwell_rt286_hw_params, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); @@ -148,6 +149,7 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } +#endif /* broadwell digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broadwell_rt286_dais[] = { @@ -160,7 +162,9 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .init = broadwell_rtd_init, +#endif .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, .dpcm_capture = 1, diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index eab1f439dd3f1a..ce7d7f6422bfb8 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -85,6 +85,7 @@ static const struct snd_soc_ops haswell_rt5640_ops = { .hw_params = haswell_rt5640_hw_params, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); @@ -103,6 +104,7 @@ static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } +#endif static struct snd_soc_dai_link haswell_rt5640_dais[] = { /* Front End DAI links */ @@ -114,7 +116,9 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .init = haswell_rtd_init, +#endif .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, .dpcm_capture = 1, From e63c8a9c8654829d50fef2bf0953be1d3e60f9b0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 9 Jan 2018 13:40:20 -0600 Subject: [PATCH 149/285] ASoC: Intel: Kconfig: expose common option between SST and SOF drivers Both drivers rely on the same module, expose it for both configurations Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 08f00058700023..9ff42b4294e755 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -117,12 +117,14 @@ config SND_SOC_INTEL_SKYLAKE GeminiLake or CannonLake platform with the DSP enabled in the BIOS then enable this option by saying Y or m. +endif ## SND_SOC_INTEL_SST_TOPLEVEL + +if SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL config SND_SOC_ACPI_INTEL_MATCH tristate select SND_SOC_ACPI if ACPI # this option controls the compilation of ACPI matching tables and # helpers and is not meant to be selected by the user. - endif ## SND_SOC_INTEL_SST_TOPLEVEL # ASoC codec drivers From fff657ff5685b06e231302582f8619e0a9379a59 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 7 Dec 2017 21:46:33 +0000 Subject: [PATCH 150/285] ASoC: Intel: select relevant machine drivers for SOF SOF can only support specific machine drivers, handle dependencies Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 24797482a3d22d..05097128a0822a 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -1,6 +1,6 @@ menuconfig SND_SOC_INTEL_MACH bool "Intel Machine drivers" - depends on SND_SOC_INTEL_SST_TOPLEVEL + depends on SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL help Intel ASoC Machine Drivers. If you have a Intel machine that has an audio controller with a DSP and I2S or DMIC port, then @@ -12,7 +12,7 @@ menuconfig SND_SOC_INTEL_MACH if SND_SOC_INTEL_MACH -if SND_SOC_INTEL_HASWELL +if SND_SOC_INTEL_HASWELL || SND_SOC_SOF_HASWELL config SND_SOC_INTEL_HASWELL_MACH tristate "Haswell Lynxpoint" @@ -24,6 +24,10 @@ config SND_SOC_INTEL_HASWELL_MACH Say Y or m if you have such a device. If unsure select "N". +endif ## SND_SOC_INTEL_HASWELL || SND_SOC_SOF_HASWELL + +if SND_SOC_INTEL_HASWELL || SND_SOC_SOF_BROADWELL + config SND_SOC_INTEL_BDW_RT5677_MACH tristate "Broadwell with RT5677 codec" depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM && GPIOLIB @@ -43,7 +47,7 @@ config SND_SOC_INTEL_BROADWELL_MACH Ultrabook platforms. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". -endif ## SND_SOC_INTEL_HASWELL +endif ## SND_SOC_INTEL_HASWELL || SND_SOC_SOF_BROADWELL if SND_SOC_INTEL_BAYTRAIL @@ -68,7 +72,7 @@ config SND_SOC_INTEL_BYT_RT5640_MACH endif ## SND_SOC_INTEL_BAYTRAIL -if SND_SST_ATOM_HIFI2_PLATFORM +if SND_SST_ATOM_HIFI2_PLATFORM || SND_SOC_SOF_BAYTRAIL config SND_SOC_INTEL_BYTCR_RT5640_MACH tristate "Baytrail and Baytrail-CR with RT5640 codec" @@ -158,6 +162,10 @@ config SND_SOC_INTEL_BYT_CHT_ES8316_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SST_ATOM_HIFI2_PLATFORM || SND_SOC_SOF_BAYTRAIL + +if SND_SST_ATOM_HIFI2_PLATFORM + config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH tristate "Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" depends on X86_INTEL_LPSS && I2C && ACPI @@ -212,6 +220,10 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_SKL + +if SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE + config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI @@ -239,6 +251,10 @@ config SND_SOC_INTEL_BXT_RT298_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE + +if SND_SOC_INTEL_SKL + config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH tristate "KBL with RT5663 and MAX98927 in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI From a3039a070980b371ab8a26d7b1f776ec11d4826d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 23 Apr 2018 12:06:09 -0500 Subject: [PATCH 151/285] ASoC: Intel: add machine driver for BXT/APL with pcm512x codec This patch adds the machine driver for the Up2 board with the Hifiberry DAC+ codec. Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 10 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_pcm512x.c | 186 +++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_pcm512x.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 05097128a0822a..6fdc798290982e 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -251,6 +251,16 @@ config SND_SOC_INTEL_BXT_RT298_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXT_PCM512x_MACH + tristate "Broxton with TI PCM512x codec" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_PCM512x_I2C + help + This adds support for ASoC machine driver for Broxton platforms + with TI PCM512x I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE if SND_SOC_INTEL_SKL diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 92b5507291af49..904a5864953ee1 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -6,6 +6,7 @@ snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o +snd-soc-sst-bxt-pcm512x-objs := bxt_pcm512x.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_pcm512x.c b/sound/soc/intel/boards/bxt_pcm512x.c new file mode 100644 index 00000000000000..5bc80853707dd2 --- /dev/null +++ b/sound/soc/intel/boards/bxt_pcm512x.c @@ -0,0 +1,186 @@ +/* + * bxt-pcm512x.c - ASoc Machine driver for Intel Baytrail and + * Cherrytrail-based platforms, with TI PCM512x codec + * + * Copyright (C) 2016 Intel Corporation + * Author: Pierre-Louis Bossart + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/pcm512x.h" +#include "../atom/sst-atom-controls.h" + +static const struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Speaker */ + {"Ext Spk", NULL, "OUTR"}, + {"Ext Spk", NULL, "OUTL"}, +}; + +static int codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = 48000; + rate->max = 48000; + channels->min = 2; + channels->max = 2; + + /* set SSP5 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int aif1_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 48000); +} + +static void aif1_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); +} + +static int init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + + snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); + snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + + return 0; +} + +static const struct snd_soc_ops aif1_ops = { + .startup = aif1_startup, + .shutdown = aif1_shutdown, +}; + +static struct snd_soc_dai_link dailink[] = { + /* CODEC<->CODEC link */ + /* back ends */ + { + .name = "SSP5-Codec", + .id = 0, + .cpu_dai_name = "sof-audio", + .platform_name = "sof-audio", + .no_pcm = 1, + .codec_dai_name = "pcm512x-hifi", + .codec_name = "i2c-104C5122:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .init = init, + .be_hw_params_fixup = codec_fixup, + .nonatomic = true, + .dpcm_playback = 1, + }, +}; + +/* SoC card */ +static struct snd_soc_card bxt_pcm512x_card = { + .name = "bxt-pcm512x", + .owner = THIS_MODULE, + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), + .dapm_widgets = dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + + /* i2c-:00 with HID being 8 chars */ +static char codec_name[SND_ACPI_I2C_ID_LEN]; + +static int bxt_pcm512x_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct snd_soc_acpi_mach *mach; + const char *i2c_name = NULL; + int dai_index = 0; + int ret_val = 0, i; + + mach = (&pdev->dev)->platform_data; + card = &bxt_pcm512x_card; + card->dev = &pdev->dev; + + /* fix index of codec dai */ + for (i = 0; i < ARRAY_SIZE(dailink); i++) { + if (!strcmp(dailink[i].codec_name, "i2c-104C5122:00")) { + dai_index = i; + break; + } + } + + /* fixup codec name based on HID */ + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); + if (i2c_name) { + snprintf(codec_name, sizeof(codec_name), + "%s%s", "i2c-", i2c_name); + dailink[dai_index].codec_name = codec_name; + } + + ret_val = devm_snd_soc_register_card(&pdev->dev, card); + if (ret_val) { + dev_err(&pdev->dev, + "snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, card); + return ret_val; +} + +static struct platform_driver bxt_pcm521x_driver = { + .driver = { + .name = "bxt-pcm512x", + }, + .probe = bxt_pcm512x_probe, +}; +module_platform_driver(bxt_pcm521x_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Broxton + PCM512x Machine driver"); +MODULE_AUTHOR("Pierre-Louis Bossart"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt-pcm512x"); From 18ba099b5a2129ecb23069dea662191e955f9dfa Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 18 May 2018 09:35:12 +0800 Subject: [PATCH 152/285] ASoC: Intel: add rt274 machine driver for cnl port rt274 machine driver from android kernel source Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 12 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt274.c | 247 +++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_rt274.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 6fdc798290982e..0037fa7bd73d0b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -309,4 +309,16 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH endif ## SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKL || SND_SOC_SOF_CANNONLAKE + +config SND_SOC_INTEL_CNL_RT274_MACH + tristate "ASoC Audio driver for CNL with RT274 in I2S Mode" + select SND_SOC_RT274 + help + This adds support for ASoC machine driver for CNL and codec RT274. This + will create an alsa sound card. Say Y if you have such a device If + unsure select "N". + +endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_CANNONLAKE + endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 904a5864953ee1..f58a9bda911ddd 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -22,6 +22,7 @@ snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o +snd-soc-cnl-rt274-objs := cnl_rt274.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -46,3 +47,4 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c new file mode 100644 index 00000000000000..6f8be5a4d508aa --- /dev/null +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -0,0 +1,247 @@ +/* + * cnl_rt274.c - ASOC Machine driver for CNL + * + * Copyright (C) 2016 Intel Corp + * Author: Guneshwor Singh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include "../../codecs/rt274.h" + +#define CNL_FREQ_OUT 24000000 +#define CNL_BE_FIXUP_RATE 48000 +#define RT274_CODEC_DAI "rt274-aif1" + +static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + int ret = 0; + int ratio = 100; + + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, + RT274_CODEC_DAI); + if (!codec_dai) + return -EINVAL; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL1, + CNL_FREQ_OUT, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(codec_dai->dev, + "failed to enable PLL1: %d\n", ret); + } + + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + return ret; +} + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin cnl_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new cnl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_pcm_stream dai_params_codec = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +static const struct snd_soc_dapm_route cnl_map[] = { + {"Headphone Jack", NULL, "HPO Pin"}, + {"MIC", NULL, "Mic Jack"}, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "codec1_out"}, + {"ssp0 Tx", NULL, "codec0_out"}, + + {"ssp0 Rx", NULL, "AIF1 Capture"}, + {"codec0_in", NULL, "ssp0 Rx"}, + + {"Headphone Jack", NULL, "Platform Clock"}, + {"MIC", NULL, "Platform Clock"}, +}; + +static struct snd_soc_jack cnl_headset; + +static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *codec_dai = runtime->codec_dai; + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + + if (ret) + return ret; + + snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(runtime->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + return 0; +} + +static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = CNL_BE_FIXUP_RATE; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "i2c-INT34C2:00"; + +struct snd_soc_dai_link cnl_rt274_dailink[] = { + { + .name = "SSP0-Codec", + .cpu_dai_name = "SSP0 Pin", + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = cnl_rt274_init, + }, + { + .name = "dmic01", + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = pname, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, +}; + +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl = { + .name = "cnl-audio", + .dai_link = cnl_rt274_dailink, + .num_links = ARRAY_SIZE(cnl_rt274_dailink), + .dapm_widgets = cnl_rt274_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_rt274_widgets), + .dapm_routes = cnl_map, + .num_dapm_routes = ARRAY_SIZE(cnl_map), + .controls = cnl_controls, + .num_controls = ARRAY_SIZE(cnl_controls), + .add_dai_link = cnl_add_dai_link, + .fully_routed = true, +}; + +static int cnl_rt274_probe(struct platform_device *pdev) +{ + snd_soc_card_cnl.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); +} + +static const struct platform_device_id cnl_board_ids[] = { + { .name = "cnl_rt274" }, + { } +}; + +static struct platform_driver cnl_rt274_driver = { + .driver = { + .name = "cnl_rt274", + .pm = &snd_soc_pm_ops, + }, + .probe = cnl_rt274_probe, + .id_table = cnl_board_ids, +}; + +module_platform_driver(cnl_rt274_driver); + +MODULE_AUTHOR("Guneshwor Singh "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_rt274"); From 1ac86e18ef5fa4a1d55d00413d86b12c740ad3b9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 May 2018 20:43:51 -0500 Subject: [PATCH 153/285] ASoC: Intel: make cnl_rt274 work with SOF (0)fix alignment issues for upstream (1)refine machine driver to make it work with sof (2)disable DMIC for it is not ready now Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cnl_rt274.c | 49 ++++++++++++++---------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 6f8be5a4d508aa..d31af7dfc1cc59 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -29,7 +29,7 @@ #define RT274_CODEC_DAI "rt274-aif1" static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) + struct snd_kcontrol *k, int event) { struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; @@ -42,8 +42,8 @@ static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, return -EINVAL; ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL1, - CNL_FREQ_OUT, - SND_SOC_CLOCK_IN); + CNL_FREQ_OUT, + SND_SOC_CLOCK_IN); if (ret) { dev_err(codec_dai->dev, "failed to enable PLL1: %d\n", ret); @@ -76,8 +76,8 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, - cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_pcm_stream dai_params_codec = { @@ -116,8 +116,9 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dai *codec_dai = runtime->codec_dai; ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET, &cnl_headset, - cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, + ARRAY_SIZE(cnl_headset_pins)); if (ret) return ret; @@ -137,42 +138,45 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) } static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = CNL_BE_FIXUP_RATE; - channels->min = channels->max = 2; + rate->min = CNL_BE_FIXUP_RATE; + rate->max = CNL_BE_FIXUP_RATE; + channels->min = 2; + channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - SNDRV_PCM_FORMAT_S24_LE); + SNDRV_PCM_FORMAT_S24_LE); return 0; } +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = 2; + channels->min = 2; + channels->max = 2; return 0; } +#endif -static const char pname[] = "0000:00:1f.3"; static const char cname[] = "i2c-INT34C2:00"; -struct snd_soc_dai_link cnl_rt274_dailink[] = { +static struct snd_soc_dai_link cnl_rt274_dailink[] = { { .name = "SSP0-Codec", .cpu_dai_name = "SSP0 Pin", .codec_name = cname, .codec_dai_name = "rt274-aif1", - .platform_name = pname, .be_hw_params_fixup = cnl_be_fixup, .ignore_pmdown_time = 1, .no_pcm = 1, @@ -182,28 +186,20 @@ struct snd_soc_dai_link cnl_rt274_dailink[] = { .dpcm_capture = 1, .init = cnl_rt274_init, }, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) { .name = "dmic01", .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", - .platform_name = pname, .ignore_suspend = 1, .no_pcm = 1, .dpcm_capture = 1, .be_hw_params_fixup = cnl_dmic_fixup, }, +#endif }; -static int -cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) -{ - link->platform_name = pname; - link->nonatomic = 1; - - return 0; -} - /* SoC card */ static struct snd_soc_card snd_soc_card_cnl = { .name = "cnl-audio", @@ -215,7 +211,6 @@ static struct snd_soc_card snd_soc_card_cnl = { .num_dapm_routes = ARRAY_SIZE(cnl_map), .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), - .add_dai_link = cnl_add_dai_link, .fully_routed = true, }; From 270eb2247f2a7accd587add2cbfd07b3a98f106b Mon Sep 17 00:00:00 2001 From: "Wagner, Steffen" Date: Tue, 15 May 2018 17:14:50 +0800 Subject: [PATCH 154/285] ASoC: tdf8532: NXP TDF8532 audio class-D amplifier driver This is a basic driver to register the codec, expose the codec DAI and control the power mode of the amplifier. Change-Id: Ie6ab037cd4d6c87e8e139b6d8af6cd4295445bf2 Signed-off-by: Mohit Sinha Signed-off-by: Steffen Wagner Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/15296 Reviewed-by: B, Jayachandran Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tdf8532.c | 379 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tdf8532.h | 101 ++++++++++ 4 files changed, 487 insertions(+) create mode 100644 sound/soc/codecs/tdf8532.c create mode 100644 sound/soc/codecs/tdf8532.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9548f63ca531c8..fbacaa0a724bd4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -157,6 +157,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5720 if I2C select SND_SOC_TAS6424 if I2C select SND_SOC_TDA7419 if I2C + select SND_SOC_TDF8532 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -954,6 +955,10 @@ config SND_SOC_TDA7419 depends on I2C select REGMAP_I2C +config SND_SOC_TDF8532 + tristate + depends on I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e849d1495308f9..40480a5243ff2e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -168,6 +168,7 @@ snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o +snd-soc-tdf8532-objs := tdf8532.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -420,6 +421,7 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o +obj-$(CONFIG_SND_SOC_TDF8532) += snd-soc-tdf8532.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c new file mode 100644 index 00000000000000..59db83da37587a --- /dev/null +++ b/sound/soc/codecs/tdf8532.c @@ -0,0 +1,379 @@ +/* + * Codec driver for NXP Semiconductors - TDF8532 + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tdf8532.h" + +static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, + va_list valist, u8 *payload) +{ + int param; + u8 len; + u8 *cmd_payload; + const u8 cmd_offset = 3; + + payload[HEADER_TYPE] = MSG_TYPE_STX; + payload[HEADER_PKTID] = dev_data->pkt_id; + + cmd_payload = &(payload[cmd_offset]); + + param = va_arg(valist, int); + len = 0; + + while (param != END) { + cmd_payload[len] = param; + + len++; + + param = va_arg(valist, int); + } + + payload[HEADER_LEN] = len; + + return len + cmd_offset; +} + +static int __tdf8532_single_write(struct tdf8532_priv *dev_data, + int dummy, ...) +{ + va_list valist; + int ret; + u8 len; + u8 payload[255]; + + va_start(valist, dummy); + + len = __tdf8532_build_pkt(dev_data, valist, payload); + + va_end(valist); + + print_hex_dump_debug("tdf8532-codec: Tx:", DUMP_PREFIX_NONE, 32, 1, + payload, len, false); + ret = i2c_master_send(dev_data->i2c, payload, len); + + dev_data->pkt_id++; + + if (ret < 0) { + dev_err(&(dev_data->i2c->dev), + "i2c send packet returned: %d\n", ret); + + return ret; + } + + return 0; +} + + +static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, + unsigned long timeout) +{ + uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; + unsigned long timeout_point = jiffies + timeout; + int ret; + + do { + ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); + if (ret < 0) + goto out; + } while (time_before(jiffies, timeout_point) && + ack_repl[0] != MSG_TYPE_ACK); + + if (ack_repl[0] != MSG_TYPE_ACK) + return -ETIME; + else + return ack_repl[2]; + +out: + return ret; +} + +static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, + char **repl_buff) +{ + int ret; + uint8_t len; + + struct device *dev = &(dev_data->i2c->dev); + + ret = tdf8532_read_wait_ack(dev_data, msecs_to_jiffies(ACK_TIMEOUT)); + + if (ret < 0) { + dev_err(dev, + "Error waiting for ACK reply: %d\n", ret); + goto out; + } + + len = ret + HEADER_SIZE; + + *repl_buff = kzalloc(len, GFP_KERNEL); + + ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); + + print_hex_dump_debug("tdf8532-codec: Rx:", DUMP_PREFIX_NONE, 32, 1, + *repl_buff, len, false); + + if (ret < 0 || ret != len) { + dev_err(dev, + "i2c recv packet returned: %d (expected: %d)\n", + ret, len); + goto out_free; + } + + return len; + +out_free: + kfree(*repl_buff); + repl_buff = NULL; +out: + return ret; +} + +static int tdf8532_get_state(struct tdf8532_priv *dev_data, + struct get_dev_status_repl **status_repl) +{ + int ret = 0; + char *repl_buff = NULL; + + ret = tdf8532_amp_write(dev_data, GET_DEV_STATUS); + if (ret < 0) + goto out; + + ret = tdf8532_single_read(dev_data, &repl_buff); + if (ret < 0) + goto out; + + *status_repl = (struct get_dev_status_repl *) repl_buff; + +out: + return ret; +} + +static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, + unsigned long timeout) +{ + unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); + int ret; + struct get_dev_status_repl *status_repl; + struct device *dev = &(dev_data->i2c->dev); + + do { + ret = tdf8532_get_state(dev_data, &status_repl); + if (ret < 0) + goto out; + + print_hex_dump_debug("tdf8532-codec: wait_state: ", + DUMP_PREFIX_NONE, 32, 1, status_repl, + 6, false); + } while (time_before(jiffies, timeout_point) + && status_repl->state != req_state); + + if (status_repl->state == req_state) + return 0; + + ret = -ETIME; + + dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", + status_repl->state, req_state, ret); + +out: + kfree(status_repl); + return ret; +} + +static int tdf8532_start_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_CONNECT); + if (ret < 0) + return ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, + CHNL_MASK(tdf8532->channels)); + + if (ret >= 0) + ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); + + return ret; +} + + +static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, + CHNL_MASK(tdf8532->channels)); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); + if (ret < 0) + goto out; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_IDLE, ACK_TIMEOUT); + +out: + return ret; +} + + +static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct snd_soc_codec *codec = dai->codec; + struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: cmd = %d\n", __func__, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = tdf8532_start_play(tdf8532); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = tdf8532_stop_play(tdf8532); + break; + } + + return ret; +} + +static int tdf8532_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(codec->dev, "%s\n", __func__); + + if (mute) + return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, + CHNL_MASK(CHNL_MAX)); + else + return tdf8532_amp_write(tdf8532, SET_CHNL_UNMUTE, + CHNL_MASK(CHNL_MAX)); +} + +static const struct snd_soc_dai_ops tdf8532_dai_ops = { + .trigger = tdf8532_dai_trigger, + .digital_mute = tdf8532_mute, +}; + +static struct snd_soc_codec_driver soc_codec_tdf8532; + +static struct snd_soc_dai_driver tdf8532_dai[] = { + { + .name = "tdf8532-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 4, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tdf8532_dai_ops, + } +}; + +static int tdf8532_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret; + struct tdf8532_priv *dev_data; + struct device *dev = &(i2c->dev); + + dev_dbg(&i2c->dev, "%s\n", __func__); + + dev_data = devm_kzalloc(dev, sizeof(struct tdf8532_priv), GFP_KERNEL); + + if (!dev_data) { + ret = -ENOMEM; + goto out; + } + + if (ret < 0) + dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); + + dev_data->i2c = i2c; + dev_data->pkt_id = 0; + dev_data->channels = 4; + + i2c_set_clientdata(i2c, dev_data); + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_tdf8532, + tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + goto out; + } + +out: + return ret; +} + +static int tdf8532_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +static const struct i2c_device_id tdf8532_i2c_id[] = { + { "tdf8532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tdf8532_i2c_id); + +#if CONFIG_ACPI +static const struct acpi_device_id tdf8532_acpi_match[] = { + {"INT34C3", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tdf8532_acpi_match); +#endif + +static struct i2c_driver tdf8532_i2c_driver = { + .driver = { + .name = "tdf8532-codec", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(tdf8532_acpi_match), + }, + .probe = tdf8532_i2c_probe, + .remove = tdf8532_i2c_remove, + .id_table = tdf8532_i2c_id, +}; + +module_i2c_driver(tdf8532_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP Semiconductors TDF8532 driver"); +MODULE_AUTHOR("Steffen Wagner "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h new file mode 100644 index 00000000000000..6e3f2c147eace9 --- /dev/null +++ b/sound/soc/codecs/tdf8532.h @@ -0,0 +1,101 @@ +/* + * tdf8532.h - Codec driver for NXP Semiconductors + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 __TDF8532_H_ +#define __TDF8532_H_ + +#define ACK_TIMEOUT 300 + +#define CHNL_MAX 5 + +#define AMP_MOD 0x80 +#define END -1 + +#define MSG_TYPE_STX 0x02 +#define MSG_TYPE_NAK 0x15 +#define MSG_TYPE_ACK 0x6 + +#define HEADER_SIZE 3 +#define HEADER_TYPE 0 +#define HEADER_PKTID 1 +#define HEADER_LEN 2 + +/* Set commands */ +#define SET_CLK_STATE 0x1A +#define CLK_DISCONNECT 0x00 +#define CLK_CONNECT 0x01 + +#define SET_CHNL_ENABLE 0x26 +#define SET_CHNL_DISABLE 0x27 + +#define SET_CHNL_MUTE 0x42 +#define SET_CHNL_UNMUTE 0x43 + +struct header_repl { + u8 msg_type; + u8 pkt_id; + u8 len; +} __packed; + +#define GET_IDENT 0xE0 + +struct get_ident_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 type_name; + u8 hw_major; + u8 hw_minor; + u8 sw_major; + u8 sw_minor; + u8 sw_sub; +} __packed; + +#define GET_ERROR 0xE2 + +struct get_error_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 last_cmd_id; + u8 error; + u8 status; +} __packed; + +#define GET_DEV_STATUS 0x80 + +enum dev_state {STATE_BOOT, STATE_IDLE, STATE_STBY, STATE_LDAG, STATE_PLAY, + STATE_PROT, STATE_SDWN, STATE_CLFA, STATE_NONE }; + +struct get_dev_status_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 state; +} __packed; + +/* Helpers */ +#define CHNL_MASK(channels) (u8)((0x00FF << channels) >> 8) + +#define tdf8532_amp_write(dev_data, ...)\ + __tdf8532_single_write(dev_data, 0, AMP_MOD, __VA_ARGS__, END) + +struct tdf8532_priv { + struct i2c_client *i2c; + u8 channels; + u8 pkt_id; +}; + +#endif From f7bae901d8f62c46f1143db913c9a5a0c6f2e55b Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 May 2018 17:14:51 +0800 Subject: [PATCH 155/285] ASoC: tdf8532: Fix compilation warnings Initialized the reported variables, listed below warning: 'ret' may be used uninitialized in this function warning: 'status_repl' may be used uninitialized in this function Change-Id: I6ca5a6e017402a582239d75959c122ffaa9f7298 Signed-off-by: Gogineni, GiribabuX Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/17572 Reviewed-by: Singh, Guneshwor O Reviewed-by: Sinha, Mohit Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 59db83da37587a..5f072079fbb76b 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -172,7 +172,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, { unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; - struct get_dev_status_repl *status_repl; + struct get_dev_status_repl *status_repl = NULL; struct device *dev = &(dev_data->i2c->dev); do { @@ -318,9 +318,6 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, goto out; } - if (ret < 0) - dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); - dev_data->i2c = i2c; dev_data->pkt_id = 0; dev_data->channels = 4; From e26299c1dad5e7af4fc4a0bb8f0f8f36b4665ceb Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 May 2018 17:14:52 +0800 Subject: [PATCH 156/285] ASoC: tdf8532: Add delay while reading a packet from I2C While doing the continuous play and stop, the codec may not be ready for I2C reading after successive writes. This triggers BE failure, because I2C reading value is incorrect. Fix this by adding 10ms delay to ensure the smooth I2C read and write. Change-Id: If918e263bc799fecc2c807229f5b4b165e011fa6 Signed-off-by: Gogineni, GiribabuX Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/20404 Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Nc, Shreyas Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 5f072079fbb76b..1860e8264ebab8 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -90,6 +90,7 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, unsigned long timeout_point = jiffies + timeout; int ret; + usleep_range(10000,20000); do { ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); if (ret < 0) From 8dd7f4a1624add0690102ffb09630503a8fa3b14 Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Tue, 15 May 2018 17:14:53 +0800 Subject: [PATCH 157/285] ASoC: tdf8532: Fix the codec status error issue on APL-GPMRB Based on the TDF8532 manual: If the wait_state result is ok, we should send CLK_DISCONNECT command to force codec from STANDBY(2) to IDLE(1). If the wait_state result is timeout, the codec state should be at Clockfail(7), we still should send CLK_DISCONNECT command force the codec from Clockfail(7) to Idle(1). Signed-off-by: Wu Zhigang Reviewed-by: Keyon Jie --- sound/soc/codecs/tdf8532.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 1860e8264ebab8..c7bdb3960eaf49 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -192,7 +192,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, ret = -ETIME; - dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", + dev_warn(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", status_repl->state, req_state, ret); out: @@ -228,8 +228,14 @@ static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) goto out; ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); - if (ret < 0) - goto out; + + /* Refer to TDF8532 manual: + * If the wait_state result is ok, we should send CLK_DISCONNECT + * command to force codec from STANDBY(2) to IDLE(1). + * If the wait_state result is timeout, the codec state should be + * at Clockfail(7), we still should send CLK_DISCONNECT command + * force the codec from Clockfail(7) to Idle(1). + */ ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); if (ret < 0) From 052f669324d26b4b161866c50bc37ccde4349633 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 22 May 2018 18:23:43 +0800 Subject: [PATCH 158/285] ASoC: Intel: Boards: Add BXTP MRB machine driver for NXP TDF8532 This is the machine driver for NXP TDF8532, from production kernel, rebased it to sof-v4.14 base. Signed-off-by: Sinha, Mohit Signed-off-by: Markus Schweikhardt Signed-off-by: Pankaj Bharadiya Signed-off-by: Kareem,Shaik Signed-off-by: Keyon Jie --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_tdf8532.c | 431 +++++++++++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_tdf8532.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 0037fa7bd73d0b..44168d6f0fd541 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -259,6 +259,15 @@ config SND_SOC_INTEL_BXT_PCM512x_MACH This adds support for ASoC machine driver for Broxton platforms with TI PCM512x I2S audio codec. Say Y or m if you have such a device. This is a recommended option. + +config SND_SOC_INTEL_BXT_TDF8532_MACH + tristate "ASoC Audio driver for BXT with TDF8532 in I2S mode" + depends on X86 && ACPI && I2C + select SND_SOC_TDF8532 + select SND_SOC_COMPRESS + help + This adds support for ASoC machine driver for Broxton IVI GP MRB platform + Say Y if you have such a device. If unsure select "N". endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index f58a9bda911ddd..8c7d03abeff397 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,7 @@ snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-bxt-pcm512x-objs := bxt_pcm512x.o +snd-soc-sst_bxt_tdf8532-objs := bxt_tdf8532.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -30,6 +31,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) += snd-soc-sst_bxt_tdf8532.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c new file mode 100644 index 00000000000000..38dc1affe9dcd5 --- /dev/null +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -0,0 +1,431 @@ +/* + * Intel Broxton-P I2S Machine Driver for IVI reference platform + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include + +static const struct snd_kcontrol_new broxton_tdf8532_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static const struct snd_soc_dapm_widget broxton_tdf8532_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DiranaCp", NULL), + SND_SOC_DAPM_HP("DiranaPb", NULL), + SND_SOC_DAPM_MIC("HdmiIn", NULL), + SND_SOC_DAPM_MIC("TestPinCp", NULL), + SND_SOC_DAPM_HP("TestPinPb", NULL), + SND_SOC_DAPM_MIC("BtHfpDl", NULL), + SND_SOC_DAPM_HP("BtHfpUl", NULL), + SND_SOC_DAPM_MIC("ModemDl", NULL), + SND_SOC_DAPM_HP("ModemUl", NULL), +}; + +static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { + /* Speaker BE connections */ + { "Speaker", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + + { "dirana_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_aux_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_tuner_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "DiranaPb", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "dirana_out"}, + + { "hdmi_ssp1_in", NULL, "ssp1 Rx"}, + { "ssp1 Rx", NULL, "HdmiIn"}, + + { "TestPin_ssp5_in", NULL, "ssp5 Rx"}, + { "ssp5 Rx", NULL, "TestPinCp"}, + + { "TestPinPb", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "TestPin_ssp5_out"}, + + { "BtHfp_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "BtHfpDl"}, + + { "BtHfpUl", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "BtHfp_ssp0_out"}, + + { "Modem_ssp3_in", NULL, "ssp3 Rx"}, + { "ssp3 Rx", NULL, "ModemDl"}, + + { "ModemUl", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "Modem_ssp3_out"}, +}; + +static int bxt_tdf8532_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* set SSP to 32 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + + return 0; +} + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Front End DAI links */ + { + .name = "Speaker Port", + .stream_name = "Speaker", + .cpu_dai_name = "Speaker Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "Dirana Capture Port", + .stream_name = "Dirana Cp", + .cpu_dai_name = "Dirana Cp Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + { + .name = "Dirana Playback Port", + .stream_name = "Dirana Pb", + .cpu_dai_name = "Dirana Pb Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "TestPin Capture Port", + .stream_name = "TestPin Cp", + .cpu_dai_name = "TestPin Cp Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + { + .name = "TestPin Playback Port", + .stream_name = "TestPin Pb", + .cpu_dai_name = "TestPin Pb Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "BtHfp Capture Port", + .stream_name = "BtHfp Cp", + .cpu_dai_name = "BtHfp Cp Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + { + .name = "BtHfp Playback Port", + .stream_name = "BtHfp Pb", + .cpu_dai_name = "BtHfp Pb Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "Modem Capture Port", + .stream_name = "Modem Cp", + .cpu_dai_name = "Modem Cp Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + { + .name = "Modem Playback Port", + .stream_name = "Modem Pb", + .cpu_dai_name = "Modem Pb Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + }, + { + .name = "HDMI Capture Port", + .stream_name = "HDMI Cp", + .cpu_dai_name = "HDMI Cp Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + { + .name = "Dirana Aux Capture Port", + .stream_name = "Dirana Aux Cp", + .cpu_dai_name = "Dirana Aux Cp Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + { + .name = "Dirana Tuner Capture Port", + .stream_name = "Dirana Tuner Cp", + .cpu_dai_name = "Dirana Tuner Cp Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + /* Probe DAI links*/ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + /* Back End DAI links */ + { + /* SSP0 - BT */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP1 - HDMI-In */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + /* SSP2 - Dirana */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + .be_hw_params_fixup = bxt_tdf8532_ssp2_fixup, + }, + { + /* SSP3 - Modem */ + .name = "SSP3-Codec", + .id = 3, + .cpu_dai_name = "SSP3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP4 - Amplifier */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .codec_name = "i2c-INT34C3:00", + .codec_dai_name = "tdf8532-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP5 - TestPin */ + .name = "SSP5-Codec", + .id = 5, + .cpu_dai_name = "SSP5 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int bxt_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:00:0e.0"; + link->nonatomic = 1; + return 0; +} + +/* broxton audio machine driver for TDF8532 */ +static struct snd_soc_card broxton_tdf8532 = { + .name = "broxton_tdf8532", + .dai_link = broxton_tdf8532_dais, + .num_links = ARRAY_SIZE(broxton_tdf8532_dais), + .controls = broxton_tdf8532_controls, + .num_controls = ARRAY_SIZE(broxton_tdf8532_controls), + .dapm_widgets = broxton_tdf8532_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_tdf8532_widgets), + .dapm_routes = broxton_tdf8532_map, + .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_tdf8532_audio_probe(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s registering %s\n", __func__, pdev->name); + broxton_tdf8532.dev = &pdev->dev; + return snd_soc_register_card(&broxton_tdf8532); +} + +static int broxton_tdf8532_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_tdf8532); + return 0; +} + +static struct platform_driver broxton_tdf8532_audio = { + .probe = broxton_tdf8532_audio_probe, + .remove = broxton_tdf8532_audio_remove, + .driver = { + .name = "bxt_tdf8532", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(broxton_tdf8532_audio) + +/* Module information */ +MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gpmrb_machine"); From 439aaa969fa4ea65fd8bd3f4fcb86aa844dc1ace Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 22 May 2018 18:23:44 +0800 Subject: [PATCH 159/285] ASoC: Intel: bxt-tdf8532: reuse machine driver for GP-MRB Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 38dc1affe9dcd5..f5c2a06ae44e6e 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -429,3 +429,4 @@ module_platform_driver(broxton_tdf8532_audio) MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:gpmrb_machine"); +MODULE_ALIAS("platform:bxt_tdf8532"); From b6666c6db6271f3d1177f77629e7b97c7a2055e3 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 28 May 2018 13:43:18 +0800 Subject: [PATCH 160/285] ASoC: Intel: bxt-tdf8532: FIX: don't use add_dai_link() for SOF We set ignore_machine for SOF soc platform driver, which will trigger overriding FEs in soc_check_tplg_fes(), but this overriding may be overidden again by add_dai_link() in bxt_tdf8532, e.g. platform_name will be modified to be "0000:00:0e.0", which is not exist in SOF. Here add #ifdef to bypass add_dai_link() for using the machine driver with SOF to fix the overridden again issue. Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index f5c2a06ae44e6e..ffd9cc5dace7a8 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -378,6 +378,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { }, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) { @@ -385,6 +386,7 @@ static int bxt_add_dai_link(struct snd_soc_card *card, link->nonatomic = 1; return 0; } +#endif /* broxton audio machine driver for TDF8532 */ static struct snd_soc_card broxton_tdf8532 = { @@ -398,7 +400,9 @@ static struct snd_soc_card broxton_tdf8532 = { .dapm_routes = broxton_tdf8532_map, .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), .fully_routed = true, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .add_dai_link = bxt_add_dai_link, +#endif }; static int broxton_tdf8532_audio_probe(struct platform_device *pdev) From 77467b910757a877eaab9df7f0f84a2c53267729 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 3 Nov 2017 20:56:42 +0000 Subject: [PATCH 161/285] ALSA: HACK: Fix rmmod crash Something appears to corrupt the runtime->status on second playback... Signed-off-by: Liam Girdwood --- sound/core/pcm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 66ac89aad68178..cee83aac0e565a 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1160,6 +1160,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) list_del_init(&pcm->list); for (cidx = 0; cidx < 2; cidx++) { for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { +#if 0 snd_pcm_stream_lock_irq(substream); if (substream->runtime) { if (snd_pcm_running(substream)) @@ -1171,6 +1172,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) wake_up(&substream->runtime->tsleep); } snd_pcm_stream_unlock_irq(substream); +#endif } } From dbf9e8c5192184d0b5e90cfaca27d753c6f3ea95 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Apr 2018 13:20:32 -0500 Subject: [PATCH 162/285] [NOT FOR UPSTREAM] ASoC: SOF: enable DEBUG by default better to use dynamic debug for products but it's really convenient Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index ece93000a1974d..b759554d497c2c 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +ccflags-y += -DDEBUG + snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o compressed.o snd-sof-spi-objs := hw-spi.o From bc4b9e39006f67b4d56c203c36469fa6e7bbdd55 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Thu, 7 Jun 2018 14:24:12 +0800 Subject: [PATCH 163/285] ASoC: Intel: replace snd_soc_codec to snd_soc_component in bxt-pcm512x Codec may be can not use now, use the component instead. Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/bxt_pcm512x.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/boards/bxt_pcm512x.c b/sound/soc/intel/boards/bxt_pcm512x.c index 5bc80853707dd2..c375d0c201582e 100644 --- a/sound/soc/intel/boards/bxt_pcm512x.c +++ b/sound/soc/intel/boards/bxt_pcm512x.c @@ -67,9 +67,10 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd, static int aif1_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *codec = rtd->codec_dai->component; - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x08); return snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, 48000); @@ -78,18 +79,20 @@ static int aif1_startup(struct snd_pcm_substream *substream) static void aif1_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *codec = rtd->codec_dai->component; - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x00); } static int init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_component *codec = rtd->codec_dai->component; - snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); - snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); + snd_soc_component_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); + snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1, + 0x08, 0x08); return 0; } From b7229c1b144a55cc63e8d6157800e396fbf97481 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 6 Jun 2018 16:11:26 +0800 Subject: [PATCH 164/285] ASoC: Intel: bytcr_rt5651: work with sof only with SSP2 AIF1 bytcr_rt5651 can work with ssp0 and ssp2, disable ssp0 support for SOF only support SSP2 AIF1 now. Also disable hard code routing for SSP2 AIF1. Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/bytcr_rt5651.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 1b1997f1d60c84..50672ade6b5b4e 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -294,6 +294,7 @@ static const struct snd_soc_dapm_route byt_rt5651_ssp0_aif2_map[] = { }; static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif1_map[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx"}, @@ -301,6 +302,7 @@ static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif1_map[] = { {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Rx", NULL, "AIF1 Capture"}, +#endif }; static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif2_map[] = { @@ -707,6 +709,7 @@ static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN]; static char byt_rt5651_codec_aif_name[12]; /* = "rt5651-aif[1|2]" */ static char byt_rt5651_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static bool is_valleyview(void) { static const struct x86_cpu_id cpu_ids[] = { @@ -718,6 +721,7 @@ static bool is_valleyview(void) return false; return true; } +#endif struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ u64 aif_value; /* 1: AIF1, 2: AIF2 */ @@ -762,6 +766,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) "%s%s", "i2c-", i2c_name); byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* * swap SSP0 if bytcr is detected * (will be overridden if DMI quirk is detected) @@ -820,6 +825,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF2; } } +#endif /* check quirks before creating card */ dmi_check_system(byt_rt5651_quirk_table); From b83debaa69d81c7b19bcf4b512547356cdda06ea Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 9 Jun 2018 23:06:25 -0700 Subject: [PATCH 165/285] ASoC: SOF: topology: free volume table while unloading pga widget Free the volume table associated with the pga widget when it is unloaded. Tested on: Minnowboard Turbot w/ RT5651 and Up^2 w/ Hifiberry DAC+ PRO Passed sanity tests on both SOF: Master SOFT: master Kernel: https://github.com/thesofproject/linux branch: topic/sof-dev Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index a2c986e4cad094..cdf21b0df89d69 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1380,19 +1380,42 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { + const struct snd_kcontrol_new *kc = NULL; + struct snd_soc_dapm_widget *widget; + struct snd_sof_control *scontrol; struct snd_sof_widget *swidget; + struct soc_mixer_control *sm; struct snd_sof_dai *dai; swidget = dobj->private; if (!swidget) return 0; - dai = swidget->private; + widget = swidget->widget; + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + dai = swidget->private; + + /* remove and free dai object */ + if (dai) { + list_del(&dai->list); + kfree(dai); + } + break; + case snd_soc_dapm_pga: + + /* get volume kcontrol */ + kc = &widget->kcontrol_news[0]; + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; - /* remove and free dai object */ - if (dai) { - list_del(&dai->list); - kfree(dai); + /* free volume table */ + kfree(scontrol->volume_table); + break; + default: + break; } /* remove and free swidget object */ From ed6cb43abcdf195e63cac698b1d65347792ace92 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 12 Jun 2018 18:51:28 -0500 Subject: [PATCH 166/285] acpi: blacklist: remove quirk for Dell XPS13 when SOF is enabled Audio works well in I2S mode with SOF (Sound Open Firmware), no need for this quirk Signed-off-by: Pierre-Louis Bossart --- drivers/acpi/blacklist.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 995c4d8922b12e..edf821bc582baa 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -91,6 +91,7 @@ static int __init dmi_enable_rev_override(const struct dmi_system_id *d) static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = { #ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* * DELL XPS 13 (2015) switches sound between HDA and I2S * depending on the ACPI _REV callback. If userspace supports @@ -105,6 +106,7 @@ static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"), }, }, +#endif { .callback = dmi_enable_rev_override, .ident = "DELL Precision 5520", From e97bc1f9585303dbdc2fe2086e36a36a824be51f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 12 Jun 2018 18:52:58 -0500 Subject: [PATCH 167/285] ASoC: Intel: common: add ACPI match for CannonLake MX98373 is used on some CNL boards Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index ec8e28e7b937aa..ae3be064e75e99 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -24,6 +24,15 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", .asoc_plat_name = "0000:00:1f.3", }, + { + .id = "MX98373", + .drv_name = "cnl_max98373", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + .sof_fw_filename = "intel/sof-cnl.ri", + .sof_tplg_filename = "intel/sof-cnl.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); From af410dad2a694bb549704de7e4f3d0b4678d8e5b Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 11 Jun 2018 23:27:38 -0700 Subject: [PATCH 168/285] ASoC: SOF: fix warning about assigning __le to u16 type This patchs addresses the warning about assigning a __le value to a u16 type. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index cdf21b0df89d69..dfaae19d466c2d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -290,7 +290,7 @@ static int get_token_u16(void *elem, void *object, u32 offset, u32 size) struct snd_soc_tplg_vendor_value_elem *velem = elem; u16 *val = object + offset; - *val = velem->value; + *val = (u16)le32_to_cpu(velem->value); return 0; } From c5450f7971dfc06c033b04c5b658b9ba83c6952e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 12:25:47 -0700 Subject: [PATCH 169/285] ASoC: SOF: topology: fix dmic pdm token offsets This patch fixes the dmic pdm token offsets to reflect the correct offset within the sof_ipc_dai_dmic_pdm_ctrl structure. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index dfaae19d466c2d..89428f1f6f445e 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -460,31 +460,31 @@ static const struct sof_topology_token dmic_tokens[] = { static const struct sof_topology_token dmic_pdm_tokens[] = { {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id), 0}, {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a), 0}, {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b), 0}, {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a), 0}, {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b), 0}, {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge), 0}, {SOF_TKN_INTEL_DMIC_PDM_SKEW, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, pdm[0]), + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew), 0}, }; From 04ca2617bce30a6a3fafdbd096a647297dfa9f7d Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 12:25:48 -0700 Subject: [PATCH 170/285] ASoC: SOF: topology: fix logic for parsing dmic pdm tokens This patch fixes the logic for parsing the dmic pdm tokens by using the private value in sof_dev to track the pdm config array element being parsed and passing the appropriate offset while getting the token. No need for a separate function to parse the pdm tokens anymore. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 104 ++++++++++++++------------------------- 1 file changed, 37 insertions(+), 67 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 89428f1f6f445e..3c03cac38b5b24 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -552,73 +552,18 @@ static void sof_parse_string_tokens(struct snd_soc_component *scomp, } } -/* helper function to parse DMIC pdm specific tokens */ -static void sof_parse_pdm_tokens(struct snd_soc_component *scomp, - struct snd_soc_tplg_vendor_value_elem *elem, - void *object, - struct sof_topology_token token) -{ - size_t size = sizeof(struct sof_ipc_dai_dmic_pdm_ctrl); - u32 offset = token.offset; - /* pdm_index determines the index in pdm[] to populate the token */ - static int pdm_index; - /* determines the offset of the item in sof_ipc_dai_dmic_pdm_ctrl */ - u32 pdm_offset; - - switch (token.token) { - case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID: - token.get_token(elem, object, offset + pdm_index * size, - token.size); - /* increment the pdm_index */ - pdm_index++; - return; - case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - enable_mic_a); - break; - case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - enable_mic_b); - break; - case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - polarity_mic_a); - break; - case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - polarity_mic_b); - break; - case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - clk_edge); - break; - case SOF_TKN_INTEL_DMIC_PDM_SKEW: - pdm_offset = offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, - skew); - break; - default: - break; - } - - /* - * offset: address pointing to pdm[0] in dmic_params - * pdm_index - 1: index of the array item to be populated - * pdm_offset: item offset in sof_ipc_dai_dmic_pdm_ctrl - * indexed by pdm_index - */ - token.get_token(elem, object, - offset + (pdm_index - 1) * size + pdm_offset, - token.size); -} - static void sof_parse_word_tokens(struct snd_soc_component *scomp, void *object, const struct sof_topology_token *tokens, int count, struct snd_soc_tplg_vendor_array *array) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_vendor_value_elem *elem; + size_t size = sizeof(struct sof_ipc_dai_dmic_pdm_ctrl); int i, j; + u32 offset; + u32 *index = NULL; /* parse element by element */ for (i = 0; i < array->num_elems; i++) { @@ -635,25 +580,43 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, if (tokens[j].token != elem->token) continue; - /* matched - now load token */ + /* pdm config array index */ + if (sdev->private) + index = (u32 *)sdev->private; + + /* matched - determine offset */ switch (tokens[j].token) { case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID: + + /* inc number of pdm array index */ + if (index) + ++(*index); case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B: case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE: case SOF_TKN_INTEL_DMIC_PDM_SKEW: - /* parse pdm specific tokens */ - sof_parse_pdm_tokens(scomp, elem, object, - tokens[j]); + + /* check if array index is valid */ + if (!index || *index == 0) { + dev_err(sdev->dev, + "error: invalid array offset\n"); + continue; + } else { + /* offset within the pdm config array */ + offset = size * (*index - 1); + } break; default: - tokens[j].get_token(elem, object, - tokens[j].offset, - tokens[j].size); + offset = 0; break; } + + /* load token */ + tokens[j].get_token(elem, object, + offset + tokens[j].offset, + tokens[j].size); } } } @@ -1604,8 +1567,14 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, /* copy the common dai config and dmic params */ memcpy(ipc_config, config, sizeof(*config)); + /* + * alloc memory for private member + * Used to track the pdm config array index currently being parsed + */ + sdev->private = kzalloc(sizeof(u32), GFP_KERNEL); + /* get DMIC PDM tokens */ - ret = sof_parse_tokens(scomp, &ipc_config->dmic, dmic_pdm_tokens, + ret = sof_parse_tokens(scomp, &ipc_config->dmic.pdm[0], dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens), private->array, private->size); if (ret != 0) { @@ -1658,6 +1627,7 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, dev_err(sdev->dev, "error: failed to set DAI config for DMIC%d\n", config->id); + kfree(sdev->private); kfree(ipc_config); return ret; From f42f8cf9ca83f48b4ac564ec2f949ba4df83ad80 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Wed, 13 Jun 2018 20:09:27 +0800 Subject: [PATCH 171/285] ASoC: SOF: refinement for HDA DMA start/stop 1. Update to follow the start/stop sequence suggested by firmware team. 2. Preserve trigger stop to fix module unload/reload fail regression issue introduced by commit: [WORKAROUND] ASoC: SOF: start HDA DMA at hw_params() stage and remove stream_trigger(). Signed-off-by: Keyon Jie --- sound/soc/sof/intel/apl.c | 1 + sound/soc/sof/intel/cnl.c | 1 + sound/soc/sof/intel/hda-pcm.c | 23 ++++++++--------------- sound/soc/sof/intel/skl.c | 1 + sound/soc/sof/pcm.c | 5 +++++ sound/soc/sof/sof-priv.h | 5 +++++ 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 98274b02943cf4..d36e30d2a8b388 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -80,6 +80,7 @@ struct snd_sof_dsp_ops sof_apl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 8f9ad191f0ce60..80d6cc772fd74a 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -208,6 +208,7 @@ struct snd_sof_dsp_ops sof_cnl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 1b50d87bd6bed4..0abb2e5778cae2 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -123,24 +123,17 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, /* disable SPIB, to enable buffer wrap for stream */ hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); - /* - * start HDA DMA here, as DSP require the DMA copy is available - * at its trigger start, which is actually before stream trigger - * start - */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, - 1 << stream->index, - 1 << stream->index); - - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset, - SOF_HDA_SD_CTL_DMA_START | - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_SD_CTL_DMA_START | - SOF_HDA_CL_DMA_SD_INT_MASK); - return stream->tag; } +int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd) +{ + struct sof_intel_hda_stream *stream = substream->runtime->private_data; + + return hda_dsp_stream_trigger(sdev, stream, cmd); +} + int hda_dsp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) { diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index 671f452e9b16d4..d4b6de626456b1 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -85,6 +85,7 @@ struct snd_sof_dsp_ops sof_skl_ops = { .host_stream_open = hda_dsp_pcm_open, .host_stream_close = hda_dsp_pcm_close, .host_stream_hw_params = hda_dsp_pcm_hw_params, + .host_stream_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 32ca808eb8624b..558d054af826cd 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -197,6 +197,7 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_sof_pcm *spcm = rtd->sof; struct sof_ipc_stream stream; struct sof_ipc_reply reply; + const struct snd_sof_dsp_ops *ops = sdev->ops; int ret = 0; /* nothing todo for BE */ @@ -231,6 +232,10 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return -EINVAL; } + /* set RUN firstly per the sequence suggested by firmware team */ + if (ops && ops->host_stream_trigger) + ret = ops->host_stream_trigger(sdev, substream, cmd); + /* send IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, sizeof(stream), &reply, sizeof(reply)); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 7e86faef5aa287..bde985f6f438be 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -116,6 +116,11 @@ struct snd_sof_dsp_ops { struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); + /* host stream trigger */ + int (*host_stream_trigger)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + int cmd); + /* FW loading */ int (*load_firmware)(struct snd_sof_dev *sof_dev, const struct firmware *fw); From aaeb5cfbfd980d0a66e24769955945da4bf99ed5 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 22:14:43 -0700 Subject: [PATCH 172/285] ASoC: uapi: sof: remove dmac id and dmac channel members from ipc host and dai comp def The firmware no longer uses the dmac id and dmac channel from topology. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-ipc.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index d1c972abd03357..e3c3de0a2d19f0 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -591,8 +591,6 @@ struct sof_ipc_comp_host { struct sof_ipc_comp_config config; enum sof_ipc_stream_direction direction; uint32_t no_irq; /* don't send periodic IRQ to host/DSP */ - uint32_t dmac_id; - uint32_t dmac_chan; uint32_t dmac_config; /* DMA engine specific */ } __attribute__((packed)); @@ -603,8 +601,6 @@ struct sof_ipc_comp_dai { enum sof_ipc_stream_direction direction; uint32_t index; enum sof_ipc_dai_type type; - uint32_t dmac_id; - uint32_t dmac_chan; uint32_t dmac_config; /* DMA engine specific */ } __attribute__((packed)); From 9b0fa7235c341643e522cc236e780db1b2afc5e6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 22:14:44 -0700 Subject: [PATCH 173/285] ASoC: uapi: sof: remove DMAC ID and DMAC channel tokens No need to pass dmac id and dmac chan from topology as the firmware does not use this info anymore. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-topology.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 5941c4cb8623a3..1a4957b7fd049d 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -34,8 +34,6 @@ #define SOF_TKN_BUF_CAPS 101 /* DAI */ -#define SOF_TKN_DAI_DMAC 151 -#define SOF_TKN_DAI_DMAC_CHAN 152 #define SOF_TKN_DAI_DMAC_CONFIG 153 #define SOF_TKN_DAI_TYPE 154 #define SOF_TKN_DAI_INDEX 155 @@ -58,8 +56,6 @@ #define SOF_TKN_SRC_RATE_OUT 301 /* PCM */ -#define SOF_TKN_PCM_DMAC 351 -#define SOF_TKN_PCM_DMAC_CHAN 352 #define SOF_TKN_PCM_DMAC_CONFIG 353 /* Generic components */ From 4bd6b227f0b8837afbac46a33b4c926542f562cc Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Jun 2018 22:14:45 -0700 Subject: [PATCH 174/285] ASoC: SOF: topology: remove dmac id and dmac channel token parsing dmac id and dmac channel tokens are no longer used by the firmware. So no need to process them. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 3c03cac38b5b24..eb9e16f643b5a5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -322,10 +322,6 @@ static const struct sof_topology_token buffer_tokens[] = { /* DAI */ static const struct sof_topology_token dai_tokens[] = { - {SOF_TKN_DAI_DMAC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, dmac_id), 0}, - {SOF_TKN_DAI_DMAC_CHAN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, dmac_chan), 0}, {SOF_TKN_DAI_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_comp_dai, dmac_config), 0}, {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, @@ -384,10 +380,6 @@ static const struct sof_topology_token tone_tokens[] = { /* PCM */ static const struct sof_topology_token pcm_tokens[] = { - {SOF_TKN_PCM_DMAC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_host, dmac_id), 0}, - {SOF_TKN_PCM_DMAC_CHAN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_host, dmac_chan), 0}, {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_comp_host, dmac_config), 0}, }; @@ -848,9 +840,8 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, return ret; } - dev_dbg(sdev->dev, "dai %s: dmac %d chan %d type %d index %d\n", - swidget->widget->name, comp_dai.dmac_id, comp_dai.dmac_chan, - comp_dai.type, comp_dai.index); + dev_dbg(sdev->dev, "dai %s: type %d index %d\n", + swidget->widget->name, comp_dai.type, comp_dai.index); sof_dbg_comp_config(scomp, &comp_dai.config); ret = sof_ipc_tx_message(sdev->ipc, comp_dai.comp.hdr.cmd, @@ -945,8 +936,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, return ret; } - dev_dbg(sdev->dev, "host %s: dmac %d chan %d\n", - swidget->widget->name, host.dmac_id, host.dmac_chan); + dev_dbg(sdev->dev, "loaded host %s\n", swidget->widget->name); sof_dbg_comp_config(scomp, &host.config); return sof_ipc_tx_message(sdev->ipc, From 59efe711a927ee8c6b694b26f2a27a8de1c5dd1b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 14 Jun 2018 21:38:15 +0100 Subject: [PATCH 175/285] ASoC: SOF: Move xtensa oops/stack dump out of SOF core Move it to sof/xtensa/core.c Signed-off-by: Liam Girdwood --- sound/soc/sof/Kconfig | 1 + sound/soc/sof/Makefile | 1 + sound/soc/sof/core.c | 134 +--------------------------- sound/soc/sof/intel/Kconfig | 1 + sound/soc/sof/intel/bdw.c | 3 + sound/soc/sof/intel/byt.c | 3 + sound/soc/sof/intel/hda.c | 3 + sound/soc/sof/intel/hsw.c | 3 + sound/soc/sof/sof-priv.h | 24 +++++ sound/soc/sof/xtensa/Kconfig | 3 + sound/soc/sof/xtensa/Makefile | 7 ++ sound/soc/sof/xtensa/core.c | 159 ++++++++++++++++++++++++++++++++++ 12 files changed, 210 insertions(+), 132 deletions(-) create mode 100644 sound/soc/sof/xtensa/Kconfig create mode 100644 sound/soc/sof/xtensa/Makefile create mode 100644 sound/soc/sof/xtensa/core.c diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 0b2e7a5fd31e81..bdae3f5969915b 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -42,3 +42,4 @@ config SND_SOC_SOF_FORCE_NOCODEC_MODE If unsure select "N". source "sound/soc/sof/intel/Kconfig" +source "sound/soc/sof/xtensa/Kconfig" diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index b759554d497c2c..e081723bf82e25 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -22,3 +22,4 @@ obj-$(SND_SOC_SOF_PLATFORM) += sof-platform-dev.o obj-$(CONFIG_SND_SOC_SOF_SPIDSP) += snd-sof-spi.o obj-$(CONFIG_SND_SOC_SOF_INTEL) += intel/ +obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/ diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 9e0e34075f4f07..b523496612d5ae 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -140,136 +140,6 @@ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_IDLE, "can't enter idle"}, }; -struct sof_exception_cause { - u32 id; - const char *msg; - const char *description; -}; - -/* From 4.4.1.5 table 4-64 Exception Causes of - * Xtensa Instruction Set Architecture (ISA) Reference Manual - */ -static const struct sof_exception_cause xtensa_exception_causes[] = { - {0, "IllegalInstructionCause", "Illegal instruction"}, - {1, "SyscallCause", "SYSCALL instruction"}, - {2, "InstructionFetchErrorCause", - "Processor internal physical address or data error during instruction fetch"}, - {3, "LoadStoreErrorCause", - "Processor internal physical address or data error during load or store"}, - {4, "Level1InterruptCause", - "Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register"}, - {5, "AllocaCause", - "MOVSP instruction, if caller’s registers are not in the register file"}, - {6, "IntegerDivideByZeroCause", - "QUOS, QUOU, REMS, or REMU divisor operand is zero"}, - {8, "PrivilegedCause", - "Attempt to execute a privileged operation when CRING ? 0"}, - {9, "LoadStoreAlignmentCause", "Load or store to an unaligned address"}, - {12, "InstrPIFDataErrorCause", - "PIF data error during instruction fetch"}, - {13, "LoadStorePIFDataErrorCause", - "Synchronous PIF data error during LoadStore access"}, - {14, "InstrPIFAddrErrorCause", - "PIF address error during instruction fetch"}, - {15, "LoadStorePIFAddrErrorCause", - "Synchronous PIF address error during LoadStore access"}, - {16, "InstTLBMissCause", "Error during Instruction TLB refill"}, - {17, "InstTLBMultiHitCause", - "Multiple instruction TLB entries matched"}, - {18, "InstFetchPrivilegeCause", - "An instruction fetch referenced a virtual address at a ring level less than CRING"}, - {20, "InstFetchProhibitedCause", - "An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch"}, - {24, "LoadStoreTLBMissCause", - "Error during TLB refill for a load or store"}, - {25, "LoadStoreTLBMultiHitCause", - "Multiple TLB entries matched for a load or store"}, - {26, "LoadStorePrivilegeCause", - "A load or store referenced a virtual address at a ring level less than CRING"}, - {28, "LoadProhibitedCause", - "A load referenced a page mapped with an attribute that does not permit loads"}, - {32, "Coprocessor0Disabled", - "Coprocessor 0 instruction when cp0 disabled"}, - {33, "Coprocessor1Disabled", - "Coprocessor 1 instruction when cp1 disabled"}, - {34, "Coprocessor2Disabled", - "Coprocessor 2 instruction when cp2 disabled"}, - {35, "Coprocessor3Disabled", - "Coprocessor 3 instruction when cp3 disabled"}, - {36, "Coprocessor4Disabled", - "Coprocessor 4 instruction when cp4 disabled"}, - {37, "Coprocessor5Disabled", - "Coprocessor 5 instruction when cp5 disabled"}, - {38, "Coprocessor6Disabled", - "Coprocessor 6 instruction when cp6 disabled"}, - {39, "Coprocessor7Disabled", - "Coprocessor 7 instruction when cp7 disabled"}, -}; - -/* only need xtensa atm */ -static void sof_arch_dsp_oops(struct snd_sof_dev *sdev, void *oops) -{ - struct sof_ipc_dsp_oops_xtensa *xoops = oops; - int i; - - dev_err(sdev->dev, "error: DSP Firmware Oops\n"); - for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { - if (xtensa_exception_causes[i].id == xoops->exccause) { - dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", - xtensa_exception_causes[i].msg, - xtensa_exception_causes[i].description); - } - } - dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", - xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); - dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", - xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); - dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", - xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); - dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", - xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); - dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", - xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); -} - -static void sof_dsp_dump_stack(struct snd_sof_dev *sdev, void *oops, - u32 *stack, u32 stack_words) -{ - struct sof_ipc_dsp_oops_xtensa *xoops = oops; - u32 stack_ptr = xoops->stack; - int i; - - dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); - - for (i = 0; i <= stack_words - 4; i += 4) { - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", - stack_ptr + i, stack[i], stack[i + 1], stack[i + 2], - stack[i + 3]); - } - - /* deal with any remaining words */ - switch (stack_words - i) { - case 0: - break; - case 1: - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x\n", - stack_ptr + stack_words - 1, stack[stack_words - 1]); - break; - case 2: - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x\n", - stack_ptr + stack_words - 2, stack[stack_words - 2], - stack[stack_words - 1]); - break; - case 3: - dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x\n", - stack_ptr + stack_words - 3, stack[stack_words - 3], - stack[stack_words - 2], stack[stack_words - 1]); - break; - default: - break; - } -} - int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, u32 tracep_code, void *oops, void *stack, size_t stack_words) @@ -301,8 +171,8 @@ int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); out: - sof_arch_dsp_oops(sdev, oops); - sof_dsp_dump_stack(sdev, oops, stack, stack_words); + sof_oops(sdev, oops); + sof_stack(sdev, oops, stack, stack_words); return -EFAULT; } EXPORT_SYMBOL(snd_sof_get_status); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 2780750d6289d7..d9b4b6a23f6021 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -3,6 +3,7 @@ config SND_SOC_SOF_INTEL depends on SND_SOC_SOF depends on SND_DMA_SGBUF select SND_SOC_INTEL_MACH + select SND_SOC_SOF_XTENSA help This adds support for Sound Open Firmware for Intel(R) platforms. Say Y if you have such a device. diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index d1dbef55e27059..eb8ee7dd72a21b 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -600,6 +600,9 @@ static int bdw_probe(struct snd_sof_dev *sdev) u32 base, size; int ret = 0; + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + /* LPE base */ mmio = platform_get_resource(pdev, IORESOURCE_MEM, desc->resindex_lpe_base); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 3103987207e4ae..fa76478d5beed9 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -531,6 +531,9 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev) u32 base, size; int ret = 0; + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + /* DSP DMA can only access low 31 bits of host memory */ ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); if (ret < 0) { diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index de138cfaa08d85..7949d6d5508025 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -311,6 +311,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) int i; int ret = 0; + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + chip = get_chip_info(pci->device); if (!chip) { dev_err(sdev->dev, "no such device supported, chip id:%x\n", diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c index b9a51021550790..e4016738123a3c 100644 --- a/sound/soc/sof/intel/hsw.c +++ b/sound/soc/sof/intel/hsw.c @@ -601,6 +601,9 @@ static int hsw_probe(struct snd_sof_dev *sdev) u32 base, size; int ret = 0; + /* set DSP arch ops */ + sdev->arch_ops = &sof_xtensa_arch_ops; + /* LPE base */ mmio = platform_get_resource(pdev, IORESOURCE_MEM, desc->resindex_lpe_base); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index bde985f6f438be..6cc851347d59d4 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -134,6 +134,12 @@ struct snd_sof_dsp_ops { int (*trace_trigger)(struct snd_sof_dev *sdev, int cmd); }; +struct sof_arch_ops { + void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops); + void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops, + u32 *stack, u32 stack_words); +}; + struct snd_sof_pdata; struct sof_ops_table { @@ -254,6 +260,7 @@ struct snd_sof_dev { struct snd_sof_pdata *pdata; const struct snd_sof_dsp_ops *ops; struct sof_intel_hda_dev *hda; /* for HDA based DSP HW */ + const struct sof_arch_ops *arch_ops; /* IPC */ struct snd_sof_ipc *ipc; @@ -434,4 +441,21 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +/* + * DSP Architectures. + */ +static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, + u32 stack_words) +{ + if (sdev->arch_ops->dsp_stack) + sdev->arch_ops->dsp_stack(sdev, oops, stack, stack_words); +} + +static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) +{ + if (sdev->arch_ops->dsp_oops) + sdev->arch_ops->dsp_oops(sdev, oops); +} + +extern const struct sof_arch_ops sof_xtensa_arch_ops; #endif diff --git a/sound/soc/sof/xtensa/Kconfig b/sound/soc/sof/xtensa/Kconfig new file mode 100644 index 00000000000000..f66f17bb03357f --- /dev/null +++ b/sound/soc/sof/xtensa/Kconfig @@ -0,0 +1,3 @@ +config SND_SOC_SOF_XTENSA + tristate + diff --git a/sound/soc/sof/xtensa/Makefile b/sound/soc/sof/xtensa/Makefile new file mode 100644 index 00000000000000..34b27be0f7be07 --- /dev/null +++ b/sound/soc/sof/xtensa/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +ccflags-y += -DDEBUG + +snd-sof-xtensa-dsp-objs := core.o + +obj-$(CONFIG_SND_SOC_SOF_XTENSA) += snd-sof-xtensa-dsp.o diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c new file mode 100644 index 00000000000000..5ca9185f5afdf5 --- /dev/null +++ b/sound/soc/sof/xtensa/core.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Pan Xiuli + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sof-priv.h" +#include "../ops.h" + +struct xtensa_exception_cause { + u32 id; + const char *msg; + const char *description; +}; + +/* From 4.4.1.5 table 4-64 Exception Causes of + * Xtensa Instruction Set Architecture (ISA) Reference Manual + */ +static const struct xtensa_exception_cause xtensa_exception_causes[] = { + {0, "IllegalInstructionCause", "Illegal instruction"}, + {1, "SyscallCause", "SYSCALL instruction"}, + {2, "InstructionFetchErrorCause", + "Processor internal physical address or data error during instruction fetch"}, + {3, "LoadStoreErrorCause", + "Processor internal physical address or data error during load or store"}, + {4, "Level1InterruptCause", + "Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register"}, + {5, "AllocaCause", + "MOVSP instruction, if caller’s registers are not in the register file"}, + {6, "IntegerDivideByZeroCause", + "QUOS, QUOU, REMS, or REMU divisor operand is zero"}, + {8, "PrivilegedCause", + "Attempt to execute a privileged operation when CRING ? 0"}, + {9, "LoadStoreAlignmentCause", "Load or store to an unaligned address"}, + {12, "InstrPIFDataErrorCause", + "PIF data error during instruction fetch"}, + {13, "LoadStorePIFDataErrorCause", + "Synchronous PIF data error during LoadStore access"}, + {14, "InstrPIFAddrErrorCause", + "PIF address error during instruction fetch"}, + {15, "LoadStorePIFAddrErrorCause", + "Synchronous PIF address error during LoadStore access"}, + {16, "InstTLBMissCause", "Error during Instruction TLB refill"}, + {17, "InstTLBMultiHitCause", + "Multiple instruction TLB entries matched"}, + {18, "InstFetchPrivilegeCause", + "An instruction fetch referenced a virtual address at a ring level less than CRING"}, + {20, "InstFetchProhibitedCause", + "An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch"}, + {24, "LoadStoreTLBMissCause", + "Error during TLB refill for a load or store"}, + {25, "LoadStoreTLBMultiHitCause", + "Multiple TLB entries matched for a load or store"}, + {26, "LoadStorePrivilegeCause", + "A load or store referenced a virtual address at a ring level less than CRING"}, + {28, "LoadProhibitedCause", + "A load referenced a page mapped with an attribute that does not permit loads"}, + {32, "Coprocessor0Disabled", + "Coprocessor 0 instruction when cp0 disabled"}, + {33, "Coprocessor1Disabled", + "Coprocessor 1 instruction when cp1 disabled"}, + {34, "Coprocessor2Disabled", + "Coprocessor 2 instruction when cp2 disabled"}, + {35, "Coprocessor3Disabled", + "Coprocessor 3 instruction when cp3 disabled"}, + {36, "Coprocessor4Disabled", + "Coprocessor 4 instruction when cp4 disabled"}, + {37, "Coprocessor5Disabled", + "Coprocessor 5 instruction when cp5 disabled"}, + {38, "Coprocessor6Disabled", + "Coprocessor 6 instruction when cp6 disabled"}, + {39, "Coprocessor7Disabled", + "Coprocessor 7 instruction when cp7 disabled"}, +}; + +/* only need xtensa atm */ +static void xtensa_dsp_oops(struct snd_sof_dev *sdev, void *oops) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + int i; + + dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { + if (xtensa_exception_causes[i].id == xoops->exccause) { + dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", + xtensa_exception_causes[i].msg, + xtensa_exception_causes[i].description); + } + } + dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", + xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); + dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", + xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); + dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", + xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); + dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", + xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); + dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", + xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); +} + +static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, + u32 stack_words) +{ + struct sof_ipc_dsp_oops_xtensa *xoops = oops; + u32 stack_ptr = xoops->stack; + int i; + + dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); + + for (i = 0; i <= stack_words - 4; i += 4) { + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + i, stack[i], stack[i + 1], stack[i + 2], + stack[i + 3]); + } + + /* deal with any remaining words */ + switch (stack_words - i) { + case 0: + break; + case 1: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x\n", + stack_ptr + stack_words - 1, stack[stack_words - 1]); + break; + case 2: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 2, stack[stack_words - 2], + stack[stack_words - 1]); + break; + case 3: + dev_err(sdev->dev, "0x%8.8x: 0x%8.8x 0x%8.8x 0x%8.8x\n", + stack_ptr + stack_words - 3, stack[stack_words - 3], + stack[stack_words - 2], stack[stack_words - 1]); + break; + default: + break; + } +} + +const struct sof_arch_ops sof_xtensa_arch_ops = { + .dsp_oops = xtensa_dsp_oops, + .dsp_stack = xtensa_stack, +}; +EXPORT_SYMBOL(sof_xtensa_arch_ops); + +MODULE_DESCRIPTION("SOF Xtensa DSP support"); +MODULE_LICENSE("Dual BSD/GPL"); From df15329163229fe3436ad914561430c20f778cf1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 15 Jun 2018 20:36:21 +0100 Subject: [PATCH 176/285] ASoC: SOF: Cleanup priv.h and add comments for upstreaming. Signed-off-by: Liam Girdwood --- sound/soc/sof/sof-priv.h | 52 +++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6cc851347d59d4..fabfcb25180940 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -49,7 +49,14 @@ struct snd_sof_ipc; struct snd_sof_debugfs_map; struct snd_soc_tplg_ops; struct snd_soc_component; +struct sof_intel_hda_dev; +struct snd_sof_pdata; +/* + * SOF DSP HW abstraction operations. + * Used to abstract DSP HW architecture and any IO busses between host CPU + * and DSP device(s). + */ struct snd_sof_dsp_ops { /* probe and remove */ int (*remove)(struct snd_sof_dev *sof_dev); @@ -134,20 +141,21 @@ struct snd_sof_dsp_ops { int (*trace_trigger)(struct snd_sof_dev *sdev, int cmd); }; +/* DSP architecture specific callbacks for oops and stack dumps */ struct sof_arch_ops { void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops); void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops, u32 *stack, u32 stack_words); }; -struct snd_sof_pdata; - +/* DSP device HW descriptor mapping between bus ID and ops */ struct sof_ops_table { const struct sof_dev_desc *desc; struct snd_sof_dsp_ops *ops; struct platform_device *(*new_data)(struct snd_sof_pdata *pdata); }; +/* FS entry for debug files that can expose DSP memories, registers */ struct snd_sof_dfsentry { struct dentry *dfsentry; size_t size; @@ -155,6 +163,7 @@ struct snd_sof_dfsentry { struct snd_sof_dev *sdev; }; +/* Debug mapping for any DSP memory or registers that can used for debug */ struct snd_sof_debugfs_map { const char *name; u32 bar; @@ -162,11 +171,28 @@ struct snd_sof_debugfs_map { u32 size; }; +/* mailbox descriptor, used for host <-> DSP IPC */ struct snd_sof_mailbox { u32 offset; size_t size; }; +/* IPC message descriptor for host <-> DSP IO */ +struct snd_sof_ipc_msg { + struct list_head list; + + /* message data */ + u32 header; + void *msg_data; + void *reply_data; + size_t msg_size; + size_t reply_size; + + wait_queue_head_t waitq; + bool complete; +}; + +/* PCM stream, mapped to FW component */ struct snd_sof_pcm_stream { u32 comp_id; struct snd_dma_buffer page_table; @@ -174,6 +200,7 @@ struct snd_sof_pcm_stream { struct snd_pcm_substream *substream; }; +/* ASLA SOF PCM device */ struct snd_sof_pcm { struct snd_sof_dev *sdev; struct snd_soc_tplg_pcm pcm; @@ -183,6 +210,7 @@ struct snd_sof_pcm { struct list_head list; /* list in sdev pcm list */ }; +/* ALSA SOF Kcontrol device */ struct snd_sof_control { struct snd_sof_dev *sdev; int comp_id; @@ -197,6 +225,7 @@ struct snd_sof_control { struct list_head list; /* list in sdev control list */ }; +/* ASoC SOF DAPM widget */ struct snd_sof_widget { struct snd_sof_dev *sdev; int comp_id; @@ -208,9 +237,10 @@ struct snd_sof_widget { struct mutex mutex; struct list_head list; /* list in sdev widget list */ - void *private; /* core does not touch this */ + void *private; /* core does not touch this */ }; +/* ASoC DAI device */ struct snd_sof_dai { struct snd_sof_dev *sdev; const char *name; @@ -220,22 +250,6 @@ struct snd_sof_dai { struct list_head list; /* list in sdev dai list */ }; -struct snd_sof_ipc_msg { - struct list_head list; - - /* message data */ - u32 header; - void *msg_data; - void *reply_data; - size_t msg_size; - size_t reply_size; - - wait_queue_head_t waitq; - bool complete; -}; - -struct sof_intel_hda_dev; - /* * SOF Device Level. */ From 51f6db7f214345ab933ce0ce965d40fd45f77fbc Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Wed, 20 Jun 2018 17:16:50 +0800 Subject: [PATCH 177/285] ASoC: Intel: replace snd_soc_codec to snd_soc_component in cnl_rt274 Codec may be can not use now, use the component instead Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/cnl_rt274.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index d31af7dfc1cc59..16daa31b8d2ca0 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -111,9 +111,9 @@ static struct snd_soc_jack cnl_headset; static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; + struct snd_soc_component *component = codec_dai->component; ret = snd_soc_card_jack_new(runtime->card, "Headset", SND_JACK_HEADSET, &cnl_headset, @@ -123,7 +123,7 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + snd_soc_component_set_jack(component, &cnl_headset, NULL); /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); From 9b843df48bbaea2a067df9d098cd700d3abd6155 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 22 Jun 2018 15:47:24 -0500 Subject: [PATCH 178/285] ASoC: Intel: common: add ACPI matching tables for ICL Signed-off-by: Pierre-Louis Bossart --- include/sound/soc-acpi-intel-match.h | 1 + sound/soc/intel/common/Makefile | 2 +- .../intel/common/soc-acpi-intel-icl-match.c | 32 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-icl-match.c diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 917ddd0f2762ea..1de9ed784c1b23 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -34,5 +34,6 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[]; #endif diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 915a34cdc8acbb..fd52419f1c814a 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -7,7 +7,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o + soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c new file mode 100644 index 00000000000000..33b441dca4d308 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-icl-match.c - tables and support for ICL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata icl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { + { + .id = "INT34C2", + .drv_name = "icl_rt274", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + .sof_fw_filename = "intel/sof-icl.ri", + .sof_tplg_filename = "intel/sof-icl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); From 2b2dae0297a54af8d1ec9fa371ddae1e08962418 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jun 2018 15:20:49 -0700 Subject: [PATCH 179/285] ASoC: uapi: sof: rename clk_id to mclk_id to make it explicit and fix the comment Some platforms have more than one MCLK's exposed. So fix the comment and rename the member to mclk_id to make it explicit to be used for passing the corrent mclk id to the firmware. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index e3c3de0a2d19f0..f78b5cb56508de 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -209,7 +209,7 @@ enum sof_ipc_dai_type { /* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */ struct sof_ipc_dai_ssp_params { uint16_t mode; // FIXME: do we need this? - uint16_t clk_id; // FIXME: do we need this? + uint16_t mclk_id; uint32_t mclk_rate; /* mclk frequency in Hz */ uint32_t fsync_rate; /* fsync frequency in Hz */ From 7f45e9bbd30f35647a5a0c37c8aacaa2040c9987 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jun 2018 15:22:43 -0700 Subject: [PATCH 180/285] ASoC: uapi: sof: add token for SSP MCLK ID Add a token for SSP_MCLK_ID which will be used to pass the MCLK ID for configuring the SSP. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-topology.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 1a4957b7fd049d..721a2b52a00b07 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -68,6 +68,7 @@ #define SOF_TKN_INTEL_SSP_MCLK_KEEP_ACTIVE 500 #define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 #define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 +#define SOF_TKN_INTEL_SSP_MCLK_ID 503 /* DMIC */ #define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 From e409473e275f4596dc5c02fd9d03d11acfd5dcc0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 20 Jun 2018 15:25:09 -0700 Subject: [PATCH 181/285] ASoC: SOF: topology: add SSP_MCLK_ID to ssp_tokens to enable parsing the mclk id from topology Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index eb9e16f643b5a5..1d4685c89f932c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -411,6 +411,9 @@ static const struct sof_topology_token ssp_tokens[] = { {SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u32, offsetof(struct sof_ipc_dai_ssp_params, fs_keep_active), 0}, + {SOF_TKN_INTEL_SSP_MCLK_ID, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, mclk_id), 0}, }; /* DMIC */ @@ -1480,7 +1483,7 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, return ret; } - ret = sof_parse_tokens(scomp, config, ssp_tokens, + ret = sof_parse_tokens(scomp, &config->ssp, ssp_tokens, ARRAY_SIZE(ssp_tokens), private->array, private->size); if (ret != 0) { @@ -1498,11 +1501,11 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, config->ssp.rx_slots = hw_config->rx_slots; config->ssp.tx_slots = hw_config->tx_slots; - dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d\n", + dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d\n", config->id, config->format, config->ssp.mclk_rate, config->ssp.bclk_rate, config->ssp.fsync_rate, config->ssp.sample_valid_bits, - config->ssp.tdm_slot_width, config->ssp.tdm_slots); + config->ssp.tdm_slot_width, config->ssp.tdm_slots, config->ssp.mclk_id); /* send message to DSP */ ret = sof_ipc_tx_message(sdev->ipc, From cb05347d9f85e18b414098d512c8287c297933d2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 25 Jun 2018 00:14:40 -0700 Subject: [PATCH 182/285] ASoC: SOF: merge sample_bits tokens with the rest of the ssp_tokens Sample bits is SSP specific. So move it to be part of SSP tokens. dai_ssp_link_tokens are not need anymore. So remove it and the corresponding call to parse the tokens. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-topology.h | 2 +- sound/soc/sof/topology.c | 18 +++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 721a2b52a00b07..096aaed4333059 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -37,7 +37,6 @@ #define SOF_TKN_DAI_DMAC_CONFIG 153 #define SOF_TKN_DAI_TYPE 154 #define SOF_TKN_DAI_INDEX 155 -#define SOF_TKN_DAI_SAMPLE_BITS 156 /* scheduling */ #define SOF_TKN_SCHED_DEADLINE 200 @@ -69,6 +68,7 @@ #define SOF_TKN_INTEL_SSP_BCLK_KEEP_ACTIVE 501 #define SOF_TKN_INTEL_SSP_FS_KEEP_ACTIVE 502 #define SOF_TKN_INTEL_SSP_MCLK_ID 503 +#define SOF_TKN_INTEL_SSP_SAMPLE_BITS 504 /* DMIC */ #define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 1d4685c89f932c..f9347384ecf3ad 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -336,11 +336,6 @@ static const struct sof_topology_token dai_link_tokens[] = { offsetof(struct sof_ipc_dai_config, type), 0}, }; -static const struct sof_topology_token dai_ssp_link_tokens[] = { - {SOF_TKN_DAI_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, -}; - /* scheduling */ static const struct sof_topology_token sched_tokens[] = { {SOF_TKN_SCHED_DEADLINE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -414,6 +409,9 @@ static const struct sof_topology_token ssp_tokens[] = { {SOF_TKN_INTEL_SSP_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, offsetof(struct sof_ipc_dai_ssp_params, mclk_id), 0}, + {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0}, }; /* DMIC */ @@ -1473,16 +1471,6 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, memset(&config->ssp, 0, sizeof(struct sof_ipc_dai_ssp_params)); config->hdr.size = size; - /* get any bespoke DAI tokens */ - ret = sof_parse_tokens(scomp, &config->ssp, dai_ssp_link_tokens, - ARRAY_SIZE(dai_ssp_link_tokens), - private->array, private->size); - if (ret != 0) { - dev_err(sdev->dev, "error: parse ssp link tokens failed %d\n", - private->size); - return ret; - } - ret = sof_parse_tokens(scomp, &config->ssp, ssp_tokens, ARRAY_SIZE(ssp_tokens), private->array, private->size); From 6c72c8b8d40d297cca1a1ba681a9cfc290e7ebed Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 22 Jun 2018 22:32:11 +0800 Subject: [PATCH 183/285] ASoC: SOF: core: register DAIs from platform drivers. Register all DAI from platform driver Signed-off-by: Liam Girdwood Signed-off-by: Keyon Jie --- sound/soc/sof/core.c | 8 +++++--- sound/soc/sof/sof-priv.h | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index b523496612d5ae..ce315938243a73 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -210,7 +210,7 @@ static int sof_probe(struct platform_device *pdev) spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); - /* set up platform and component drivers */ + /* set up platform component driver */ snd_sof_new_platform_drv(sdev); snd_sof_new_dai_drv(sdev); @@ -271,14 +271,16 @@ static int sof_probe(struct platform_device *pdev) goto fw_run_err; } - ret = snd_soc_register_component(&pdev->dev, sdev->cmpnt_drv, - &sdev->dai_drv, sdev->num_dai); + ret = snd_soc_register_component(&pdev->dev, sdev->cmpnt_drv, + sdev->ops->dai_drv->drv, + sdev->ops->dai_drv->num_drv); if (ret < 0) { dev_err(sdev->dev, "error: failed to register DSP DAI driver %d\n", ret); goto comp_err; } + /* init DMA trace */ ret = snd_sof_init_trace(sdev); if (ret < 0) { dev_warn(sdev->dev, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index fabfcb25180940..271f649029a956 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -43,6 +43,14 @@ /* max number of FE PCMs before BEs */ #define SOF_BE_PCM_BASE 16 +/* convenience constructor for DAI driver streams */ +#define SOF_DAI_STREAM(sname, scmin, scmax, srates, sfmt) \ + {.stream_name = sname, .channels_min = scmin, .channels_max = scmax, \ + .rates = srates, .formats = sfmt} + +#define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) + struct snd_sof_dev; struct snd_sof_ipc_msg; struct snd_sof_ipc; @@ -52,6 +60,11 @@ struct snd_soc_component; struct sof_intel_hda_dev; struct snd_sof_pdata; +struct snd_sof_dai_drv { + struct snd_soc_dai_driver *drv; + int num_drv; +}; + /* * SOF DSP HW abstraction operations. * Used to abstract DSP HW architecture and any IO busses between host CPU @@ -139,6 +152,9 @@ struct snd_sof_dsp_ops { int (*trace_init)(struct snd_sof_dev *sdev, u32 *stream_tag); int (*trace_release)(struct snd_sof_dev *sdev); int (*trace_trigger)(struct snd_sof_dev *sdev, int cmd); + + /* DAI ops */ + struct snd_sof_dai_drv *dai_drv; }; /* DSP architecture specific callbacks for oops and stack dumps */ @@ -263,8 +279,6 @@ struct snd_sof_dev { /* ASoC components */ struct snd_soc_platform_driver plat_drv; const struct snd_soc_component_driver *cmpnt_drv; - struct snd_soc_dai_driver dai_drv; - int num_dai; /* DSP firmware boot */ wait_queue_head_t boot_wait; From a7fecd5314283f03f179e9d810e74845c68243cb Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 17 Jun 2018 22:07:49 +0100 Subject: [PATCH 184/285] ASoC: SOF: pcm: remove DAI driver Signed-off-by: Liam Girdwood Signed-off-by: Keyon Jie --- sound/soc/sof/pcm.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 558d054af826cd..9a7f16eb0f3057 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -625,30 +625,12 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->topology_name_prefix = "sof"; } -static const struct snd_soc_dai_ops sof_dai_ops = { -}; - static const struct snd_soc_component_driver sof_dai_component = { - .name = "sof-dai", + .name = "sof-dai", }; -#define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) - void snd_sof_new_dai_drv(struct snd_sof_dev *sdev) { - struct snd_soc_dai_driver *dd = &sdev->dai_drv; - //struct snd_sof_pdata *plat_data = sdev->pdata; - sdev->cmpnt_drv = &sof_dai_component; - dd->playback.channels_min = 1; - dd->playback.channels_max = 16; - dd->playback.rates = SNDRV_PCM_RATE_8000_192000; - dd->playback.formats = SOF_FORMATS; - dd->capture.channels_min = 1; - dd->capture.channels_max = 16; - dd->capture.rates = SNDRV_PCM_RATE_8000_192000; - dd->capture.formats = SOF_FORMATS; - dd->ops = &sof_dai_ops; - sdev->num_dai = 1; } + From 47c8509a9a3a6222355d7678261ca9b1ba2f7b7f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 17 Jun 2018 22:07:50 +0100 Subject: [PATCH 185/285] ASoC: SOF: nocodec: give DAI driver more meaningful name Signed-off-by: Liam Girdwood --- sound/soc/sof/nocodec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index 0685d29f9d28b1..dab4e2e287914f 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -63,7 +63,7 @@ static struct snd_soc_dai_link sof_nocodec_dais[] = { .name = "NoCodec", .id = 0, .init = nocodec_rtd_init, - .cpu_dai_name = "sof-audio", + .cpu_dai_name = "sof-nocodec-dai", .platform_name = "sof-audio", .no_pcm = 1, .codec_dai_name = "snd-soc-dummy-dai", From 15dcfb28879ccb5fb9d54395ceff084c25619474 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 17 Jun 2018 22:07:51 +0100 Subject: [PATCH 186/285] ASoC: SOF: intel: SKL, CNL, APL platform DAIs Signed-off-by: Liam Girdwood Signed-off-by: Keyon Jie --- sound/soc/sof/intel/Makefile | 2 +- sound/soc/sof/intel/apl.c | 3 + sound/soc/sof/intel/cnl.c | 3 + sound/soc/sof/intel/hda-dai.c | 118 ++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 3 + sound/soc/sof/intel/skl.c | 3 + 6 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/intel/hda-dai.c diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index dfeb4fcf217b90..7b5784d88fd154 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -6,7 +6,7 @@ snd-sof-intel-byt-objs := byt.o snd-sof-intel-hsw-objs := hsw.o snd-sof-intel-bdw-objs := bdw.o snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ - hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ + hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o hda-dai.o\ skl.o apl.o cnl.o obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-intel-byt.o diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index d36e30d2a8b388..9db5a246033c15 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -92,5 +92,8 @@ struct snd_sof_dsp_ops sof_apl_ops = { .trace_init = hda_dsp_trace_init, .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + + /* DAI drivers */ + .dai_drv = &hda_dai_drv, }; EXPORT_SYMBOL(sof_apl_ops); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 80d6cc772fd74a..99c80d772ad6d6 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -220,5 +220,8 @@ struct snd_sof_dsp_ops sof_cnl_ops = { .trace_init = hda_dsp_trace_init, .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + + /* DAI drivers */ + .dai_drv = &hda_dai_drv, }; EXPORT_SYMBOL(sof_cnl_ops); diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c new file mode 100644 index 00000000000000..091fcc1a7e66a0 --- /dev/null +++ b/sound/soc/sof/intel/hda-dai.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Authors: Keyon Jie + */ + +#include +#include "../sof-priv.h" + +#define SKL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* + * common dai driver for skl+ platforms. + * some products who use this DAI array only physically have a subset of + * the DAIs, but no harm is done here by adding the whole set. + */ +static struct snd_soc_dai_driver skl_dai[] = { +{ + .name = "SSP0 Pin", + .playback = SOF_DAI_STREAM("ssp0 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp0 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP1 Pin", + .playback = SOF_DAI_STREAM("ssp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp1 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP2 Pin", + .playback = SOF_DAI_STREAM("ssp2 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp2 Rx", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP3 Pin", + .playback = SOF_DAI_STREAM("ssp3 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp3 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP4 Pin", + .playback = SOF_DAI_STREAM("ssp4 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp4 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "SSP5 Pin", + .playback = SOF_DAI_STREAM("ssp5 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("ssp5 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "DMIC01 Pin", + .capture = SOF_DAI_STREAM("DMIC01 Rx", 1, 4, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "DMIC16k Pin", + .capture = SOF_DAI_STREAM("DMIC16k Rx", 1, 4, + SNDRV_PCM_RATE_16000, SKL_FORMATS), +}, +{ + .name = "iDisp1 Pin", + .playback = SOF_DAI_STREAM("iDisp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "iDisp2 Pin", + .playback = SOF_DAI_STREAM("iDisp2 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "iDisp3 Pin", + .playback = SOF_DAI_STREAM("iDisp3 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "Analog Codec DAI", + .playback = SOF_DAI_STREAM("Analog Codec Playback", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("Analog Codec Capture", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "Digital Codec DAI", + .playback = SOF_DAI_STREAM("Digital Codec Playback", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("Digital Codec Capture", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +{ + .name = "Alt Analog Codec DAI", + .playback = SOF_DAI_STREAM("Alt Analog Codec Playback", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), + .capture = SOF_DAI_STREAM("Alt Analog Codec Capture", 1, 16, + SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), +}, +}; + +struct snd_sof_dai_drv hda_dai_drv = { + .drv = skl_dai, + .num_drv = ARRAY_SIZE(skl_dai) +}; +EXPORT_SYMBOL(hda_dai_drv); + diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 5eb677c601dd4f..75f5b409d743bc 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -494,6 +494,9 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag); int hda_dsp_trace_release(struct snd_sof_dev *sdev); int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); +/* common dai driver */ +extern struct snd_sof_dai_drv hda_dai_drv; + /* * Platform Specific HW abstraction Ops. */ diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index d4b6de626456b1..adb85a579b697e 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -97,5 +97,8 @@ struct snd_sof_dsp_ops sof_skl_ops = { .trace_init = hda_dsp_trace_init, .trace_release = hda_dsp_trace_release, .trace_trigger = hda_dsp_trace_trigger, + + /* DAI drivers */ + .dai_drv = &hda_dai_drv, }; EXPORT_SYMBOL(sof_skl_ops); From cc7af20388fb68c2813fa12f33c4b36d8821071d Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 17 Jun 2018 22:07:52 +0100 Subject: [PATCH 187/285] ASoC: SOF: Intel BDW platform DAIs Signed-off-by: Liam Girdwood Signed-off-by: Keyon Jie --- sound/soc/sof/intel/bdw.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index eb8ee7dd72a21b..47d3896329d25b 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -709,6 +709,32 @@ static int bdw_remove(struct snd_sof_dev *sdev) return 0; } +#define BDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Broadwell DAIs */ +static struct snd_soc_dai_driver bdw_dai[] = { +{ + .name = "ssp0-port", + .playback = SOF_DAI_STREAM("ssp0 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BDW_FORMATS), + .capture = SOF_DAI_STREAM("ssp0 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BDW_FORMATS), +}, +{ + .name = "ssp1-port", + .playback = SOF_DAI_STREAM("ssp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BDW_FORMATS), + .capture = SOF_DAI_STREAM("ssp1 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BDW_FORMATS), +}, +}; + +struct snd_sof_dai_drv bdw_dai_drv = { + .drv = bdw_dai, + .num_drv = ARRAY_SIZE(bdw_dai) +}; + /* broadwell ops */ struct snd_sof_dsp_ops sof_bdw_ops = { /*Device init */ @@ -750,6 +776,9 @@ struct snd_sof_dsp_ops sof_bdw_ops = { /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + + /* DAI drivers */ + .dai_drv = &bdw_dai_drv, }; EXPORT_SYMBOL(sof_bdw_ops); From 429569835404a6b5694c579afab2ee66c34dd4f9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 17 Jun 2018 22:07:53 +0100 Subject: [PATCH 188/285] ASoC: SOF: Intel BYT platform DAIs Signed-off-by: Liam Girdwood Signed-off-by: Keyon Jie --- sound/soc/sof/intel/byt.c | 66 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index fa76478d5beed9..77a2c7a041cc96 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -750,6 +750,66 @@ static int byt_remove(struct snd_sof_dev *sdev) return byt_acpi_remove(sdev); } +#define BYT_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Baytrail DAIs */ +static struct snd_soc_dai_driver byt_dai[] = { +{ + .name = "ssp0-port", + .playback = SOF_DAI_STREAM("ssp0 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp0 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp1-port", + .playback = SOF_DAI_STREAM("ssp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp1 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp2-port", + .playback = SOF_DAI_STREAM("ssp2 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp2 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp3-port", + .playback = SOF_DAI_STREAM("ssp3 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp3 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp4-port", + .playback = SOF_DAI_STREAM("ssp4 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp4 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +{ + .name = "ssp5-port", + .playback = SOF_DAI_STREAM("ssp5 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), + .capture = SOF_DAI_STREAM("ssp5 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, BYT_FORMATS), +}, +}; + +struct snd_sof_dai_drv byt_dai_drv = { + .drv = byt_dai, + .num_drv = 3, /* we have only 3 SSPs on byt*/ +}; + +struct snd_sof_dai_drv cht_dai_drv = { + .drv = byt_dai, + /* all 6 SSPs may be available for cherrytrail */ + .num_drv = ARRAY_SIZE(byt_dai), +}; + /* baytrail ops */ struct snd_sof_dsp_ops sof_byt_ops = { /* device init */ @@ -795,6 +855,9 @@ struct snd_sof_dsp_ops sof_byt_ops = { /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + + /* DAI drivers */ + .dai_drv = &byt_dai_drv, }; EXPORT_SYMBOL(sof_byt_ops); @@ -843,6 +906,9 @@ struct snd_sof_dsp_ops sof_cht_ops = { /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + + /* DAI drivers */ + .dai_drv = &cht_dai_drv, }; EXPORT_SYMBOL(sof_cht_ops); From 4d86f98c721a895122aa9c0637a22dd0d7e04da8 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 17 Jun 2018 22:07:54 +0100 Subject: [PATCH 189/285] ASoC: SOF: Intel HSW platform DAIs Signed-off-by: Liam Girdwood Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hsw.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c index e4016738123a3c..5e2c50b47b7502 100644 --- a/sound/soc/sof/intel/hsw.c +++ b/sound/soc/sof/intel/hsw.c @@ -709,6 +709,32 @@ static int hsw_remove(struct snd_sof_dev *sdev) return 0; } +#define HSW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Haswell DAIs */ +static struct snd_soc_dai_driver hsw_dai[] = { +{ + .name = "ssp0-port", + .playback = SOF_DAI_STREAM("ssp0 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, HSW_FORMATS), + .capture = SOF_DAI_STREAM("ssp0 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, HSW_FORMATS), +}, +{ + .name = "ssp1-port", + .playback = SOF_DAI_STREAM("ssp1 Tx", 1, 8, + SNDRV_PCM_RATE_8000_192000, HSW_FORMATS), + .capture = SOF_DAI_STREAM("ssp1 Rx", 1, 8, + SNDRV_PCM_RATE_8000_192000, HSW_FORMATS), +}, +}; + +struct snd_sof_dai_drv hsw_dai_drv = { + .drv = hsw_dai, + .num_drv = ARRAY_SIZE(hsw_dai) +}; + /* haswell ops */ struct snd_sof_dsp_ops sof_hsw_ops = { /*Device init */ @@ -751,6 +777,9 @@ struct snd_sof_dsp_ops sof_hsw_ops = { /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* DAI drivers */ + .dai_drv = &hsw_dai_drv, + }; EXPORT_SYMBOL(sof_hsw_ops); From 340ada00f01525c3b833dbbc0889f2cf3d009090 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 22 Jun 2018 21:24:05 +0800 Subject: [PATCH 190/285] ASoC: SOF: nocodec: create BE DAI links based on platform dai drivers This patch create nocodec BE DAI links based on platform dai drivers, with it, we can support multiple SSP/BEs for nocodec mode. Signed-off-by: Keyon Jie --- include/sound/sof.h | 7 +++- sound/soc/sof/Makefile | 2 +- sound/soc/sof/nocodec.c | 67 ++++++++++++------------------------ sound/soc/sof/sof-acpi-dev.c | 4 +-- sound/soc/sof/sof-pci-dev.c | 4 +-- sound/soc/sof/utils.c | 55 +++++++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 51 deletions(-) create mode 100644 sound/soc/sof/utils.c diff --git a/include/sound/sof.h b/include/sound/sof.h index f6056247c3a75f..e522e13e7a18d7 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -76,6 +77,10 @@ struct sof_dev_desc { int sof_nocodec_setup(struct device *dev, struct snd_sof_pdata *sof_pdata, struct snd_soc_acpi_mach *mach, - const struct sof_dev_desc *desc); + const struct sof_dev_desc *desc, + struct snd_sof_dsp_ops *ops); +int sof_bes_setup(struct device *dev, struct snd_sof_dsp_ops *ops, + struct snd_soc_dai_link *links, int link_num, + struct snd_soc_card *card); #endif diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index e081723bf82e25..b3ba39d61b2a9e 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -3,7 +3,7 @@ ccflags-y += -DDEBUG snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o compressed.o + control.o trace.o compressed.o utils.o snd-sof-spi-objs := hw-spi.o snd-sof-pci-objs := sof-pci-dev.o diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index dab4e2e287914f..0e1e271fb583a3 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -20,12 +20,21 @@ #include #include #include +#include "sof-priv.h" + +static struct snd_soc_card sof_nocodec_card = { + .name = "sof-nocodec", +}; int sof_nocodec_setup(struct device *dev, struct snd_sof_pdata *sof_pdata, struct snd_soc_acpi_mach *mach, - const struct sof_dev_desc *desc) + const struct sof_dev_desc *desc, + struct snd_sof_dsp_ops *ops) { + struct snd_soc_dai_link *links; + int ret; + if (!mach) return -EINVAL; @@ -35,54 +44,22 @@ int sof_nocodec_setup(struct device *dev, mach->sof_fw_filename = desc->nocodec_fw_filename; mach->sof_tplg_filename = desc->nocodec_tplg_filename; - return 0; -} -EXPORT_SYMBOL(sof_nocodec_setup); - -static int sof_nocodec_codec_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - // TODO: read this from topology - return 0; -} - -static struct snd_soc_ops sof_nocodec_ops = {}; + /* create dummy BE dai_links */ + links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * + ops->dai_drv->num_drv, GFP_KERNEL); + if (!links) + return -ENOMEM; -static int nocodec_rtd_init(struct snd_soc_pcm_runtime *rtd) -{ - snd_soc_set_dmi_name(rtd->card, NULL); + ret = sof_bes_setup(dev, ops, links, ops->dai_drv->num_drv, + &sof_nocodec_card); + if (ret) { + kfree(links); + return ret; + } return 0; } - -/* we just set some BEs - FE provided by topology */ -static struct snd_soc_dai_link sof_nocodec_dais[] = { - /* Back End DAI links */ - { - /* SSP0 - Codec */ - .name = "NoCodec", - .id = 0, - .init = nocodec_rtd_init, - .cpu_dai_name = "sof-nocodec-dai", - .platform_name = "sof-audio", - .no_pcm = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .ops = &sof_nocodec_ops, - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .ignore_suspend = 1, - .be_hw_params_fixup = sof_nocodec_codec_fixup, - .dpcm_playback = 1, - .dpcm_capture = 1, - }, -}; - -static struct snd_soc_card sof_nocodec_card = { - .name = "sof-nocodec", - .dai_link = sof_nocodec_dais, - .num_links = ARRAY_SIZE(sof_nocodec_dais), -}; +EXPORT_SYMBOL(sof_nocodec_setup); static int sof_nocodec_probe(struct platform_device *pdev) { diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index dd71297e8c2558..fec3b012ae7191 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -246,7 +246,7 @@ static int sof_acpi_probe(struct platform_device *pdev) /* force nocodec mode */ dev_warn(dev, "Force to use nocodec mode\n"); mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); if (ret < 0) return ret; #else @@ -257,7 +257,7 @@ static int sof_acpi_probe(struct platform_device *pdev) /* fallback to nocodec mode */ dev_warn(dev, "No matching ASoC machine driver found - using nocodec\n"); mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); if (ret < 0) return ret; #else diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 7a2d48e0d34764..83c71ac3418eb8 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -226,7 +226,7 @@ static int sof_pci_probe(struct pci_dev *pci, /* force nocodec mode */ dev_warn(dev, "Force to use nocodec mode\n"); mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); if (ret < 0) return ret; #else @@ -237,7 +237,7 @@ static int sof_pci_probe(struct pci_dev *pci, /* fallback to nocodec mode */ dev_warn(dev, "No matching ASoC machine driver found - using nocodec\n"); mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - ret = sof_nocodec_setup(dev, sof_pdata, mach, desc); + ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); if (ret < 0) return ret; #else diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c new file mode 100644 index 00000000000000..8f2116a453526e --- /dev/null +++ b/sound/soc/sof/utils.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Keyon Jie + */ + +#include +#include +#include +#include "sof-priv.h" + +int sof_bes_setup(struct device *dev, struct snd_sof_dsp_ops *ops, + struct snd_soc_dai_link *links, int link_num, + struct snd_soc_card *card) +{ + char name[32]; + int i; + + if (!ops || !links || !card) + return -EINVAL; + + /* set up BE dai_links */ + for (i = 0; i < link_num; i++) { + snprintf(name, 32, "NoCodec-%d", i); + links[i].name = kmemdup(name, sizeof(name), GFP_KERNEL); + if (!links[i].name) + goto no_mem; + + links[i].id = i; + links[i].no_pcm = 1; + links[i].cpu_dai_name = ops->dai_drv->drv[i].name; + links[i].platform_name = "sof-audio"; + links[i].codec_dai_name = "snd-soc-dummy-dai"; + links[i].codec_name = "snd-soc-dummy"; + links[i].dpcm_playback = 1; + links[i].dpcm_capture = 1; + } + + card->dai_link = links; + card->num_links = link_num; + + return 0; +no_mem: + /* free allocated memories and return error */ + for (; i > 0; i--) + kfree(links[i - 1].name); + + return -ENOMEM; +} +EXPORT_SYMBOL(sof_bes_setup); + From 327073bc259710ba5cd789bb3d8a14bbe123600d Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 22 Jun 2018 21:28:33 +0800 Subject: [PATCH 191/285] ASoC: core: don't override too much for BE dai_links 1. Don't override cpu_dai_name, we will reuse those defined in machine driver. 2. We should keep dpcm_playback and dpcm_capture defined for BEs, not both of them are supported by BEs sometimes. Signed-off-by: Keyon Jie --- sound/soc/soc-core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a0160a409e4094..b32ede7de9fd9c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2126,12 +2126,9 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) /* override platform */ dai_link->platform_name = platform->component.name; - dai_link->cpu_dai_name = platform->component.name; /* convert non BE into BE */ dai_link->no_pcm = 1; - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; /* override any BE fixups */ dai_link->be_hw_params_fixup = From 5be0d42728598f6448e015b5ef1bec6554f7a8cd Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Wed, 20 Jun 2018 16:50:51 +0800 Subject: [PATCH 192/285] ASoC: Intel: bxt-tdf8532: change probe and trace buffer dai_links to dynamic We should use .dynamic for all FE dai_links, so change probe and trace buffer ones here to align to that. Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index ffd9cc5dace7a8..1e0e633d493b6d 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -266,6 +266,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .init = NULL, .nonatomic = 1, + .dynamic = 1, }, { .name = "Bxt Compress Probe capture", @@ -276,6 +277,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .init = NULL, .nonatomic = 1, + .dynamic = 1, }, /* Trace Buffer DAI links */ { @@ -287,6 +289,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .capture_only = true, .ignore_suspend = 1, + .dynamic = 1, }, { .name = "Bxt Trace Buffer1", @@ -297,6 +300,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .capture_only = true, .ignore_suspend = 1, + .dynamic = 1, }, /* Back End DAI links */ { From 7db42ba7dbc1f051d36551b2bcdcd235f3f4a6e9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 12:22:53 -0500 Subject: [PATCH 193/285] ASoC: Intel: revert code related to TDF8532 Clean-up before re-adding latest code Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/Kconfig | 5 - sound/soc/codecs/Makefile | 2 - sound/soc/codecs/tdf8532.c | 383 ----------------------- sound/soc/codecs/tdf8532.h | 101 ------ sound/soc/intel/boards/Kconfig | 10 - sound/soc/intel/boards/Makefile | 2 - sound/soc/intel/boards/bxt_tdf8532.c | 440 --------------------------- 7 files changed, 943 deletions(-) delete mode 100644 sound/soc/codecs/tdf8532.c delete mode 100644 sound/soc/codecs/tdf8532.h delete mode 100644 sound/soc/intel/boards/bxt_tdf8532.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index fbacaa0a724bd4..9548f63ca531c8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -157,7 +157,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5720 if I2C select SND_SOC_TAS6424 if I2C select SND_SOC_TDA7419 if I2C - select SND_SOC_TDF8532 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -955,10 +954,6 @@ config SND_SOC_TDA7419 depends on I2C select REGMAP_I2C -config SND_SOC_TDF8532 - tristate - depends on I2C - config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 40480a5243ff2e..e849d1495308f9 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -168,7 +168,6 @@ snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o -snd-soc-tdf8532-objs := tdf8532.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -421,7 +420,6 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o -obj-$(CONFIG_SND_SOC_TDF8532) += snd-soc-tdf8532.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c deleted file mode 100644 index c7bdb3960eaf49..00000000000000 --- a/sound/soc/codecs/tdf8532.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Codec driver for NXP Semiconductors - TDF8532 - * Copyright (c) 2017, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tdf8532.h" - -static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, - va_list valist, u8 *payload) -{ - int param; - u8 len; - u8 *cmd_payload; - const u8 cmd_offset = 3; - - payload[HEADER_TYPE] = MSG_TYPE_STX; - payload[HEADER_PKTID] = dev_data->pkt_id; - - cmd_payload = &(payload[cmd_offset]); - - param = va_arg(valist, int); - len = 0; - - while (param != END) { - cmd_payload[len] = param; - - len++; - - param = va_arg(valist, int); - } - - payload[HEADER_LEN] = len; - - return len + cmd_offset; -} - -static int __tdf8532_single_write(struct tdf8532_priv *dev_data, - int dummy, ...) -{ - va_list valist; - int ret; - u8 len; - u8 payload[255]; - - va_start(valist, dummy); - - len = __tdf8532_build_pkt(dev_data, valist, payload); - - va_end(valist); - - print_hex_dump_debug("tdf8532-codec: Tx:", DUMP_PREFIX_NONE, 32, 1, - payload, len, false); - ret = i2c_master_send(dev_data->i2c, payload, len); - - dev_data->pkt_id++; - - if (ret < 0) { - dev_err(&(dev_data->i2c->dev), - "i2c send packet returned: %d\n", ret); - - return ret; - } - - return 0; -} - - -static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, - unsigned long timeout) -{ - uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; - unsigned long timeout_point = jiffies + timeout; - int ret; - - usleep_range(10000,20000); - do { - ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); - if (ret < 0) - goto out; - } while (time_before(jiffies, timeout_point) && - ack_repl[0] != MSG_TYPE_ACK); - - if (ack_repl[0] != MSG_TYPE_ACK) - return -ETIME; - else - return ack_repl[2]; - -out: - return ret; -} - -static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, - char **repl_buff) -{ - int ret; - uint8_t len; - - struct device *dev = &(dev_data->i2c->dev); - - ret = tdf8532_read_wait_ack(dev_data, msecs_to_jiffies(ACK_TIMEOUT)); - - if (ret < 0) { - dev_err(dev, - "Error waiting for ACK reply: %d\n", ret); - goto out; - } - - len = ret + HEADER_SIZE; - - *repl_buff = kzalloc(len, GFP_KERNEL); - - ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); - - print_hex_dump_debug("tdf8532-codec: Rx:", DUMP_PREFIX_NONE, 32, 1, - *repl_buff, len, false); - - if (ret < 0 || ret != len) { - dev_err(dev, - "i2c recv packet returned: %d (expected: %d)\n", - ret, len); - goto out_free; - } - - return len; - -out_free: - kfree(*repl_buff); - repl_buff = NULL; -out: - return ret; -} - -static int tdf8532_get_state(struct tdf8532_priv *dev_data, - struct get_dev_status_repl **status_repl) -{ - int ret = 0; - char *repl_buff = NULL; - - ret = tdf8532_amp_write(dev_data, GET_DEV_STATUS); - if (ret < 0) - goto out; - - ret = tdf8532_single_read(dev_data, &repl_buff); - if (ret < 0) - goto out; - - *status_repl = (struct get_dev_status_repl *) repl_buff; - -out: - return ret; -} - -static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, - unsigned long timeout) -{ - unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); - int ret; - struct get_dev_status_repl *status_repl = NULL; - struct device *dev = &(dev_data->i2c->dev); - - do { - ret = tdf8532_get_state(dev_data, &status_repl); - if (ret < 0) - goto out; - - print_hex_dump_debug("tdf8532-codec: wait_state: ", - DUMP_PREFIX_NONE, 32, 1, status_repl, - 6, false); - } while (time_before(jiffies, timeout_point) - && status_repl->state != req_state); - - if (status_repl->state == req_state) - return 0; - - ret = -ETIME; - - dev_warn(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", - status_repl->state, req_state, ret); - -out: - kfree(status_repl); - return ret; -} - -static int tdf8532_start_play(struct tdf8532_priv *tdf8532) -{ - int ret; - - ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_CONNECT); - if (ret < 0) - return ret; - - ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, - CHNL_MASK(tdf8532->channels)); - - if (ret >= 0) - ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); - - return ret; -} - - -static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) -{ - int ret; - - ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, - CHNL_MASK(tdf8532->channels)); - if (ret < 0) - goto out; - - ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); - - /* Refer to TDF8532 manual: - * If the wait_state result is ok, we should send CLK_DISCONNECT - * command to force codec from STANDBY(2) to IDLE(1). - * If the wait_state result is timeout, the codec state should be - * at Clockfail(7), we still should send CLK_DISCONNECT command - * force the codec from Clockfail(7) to Idle(1). - */ - - ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); - if (ret < 0) - goto out; - - ret = tdf8532_wait_state(tdf8532, STATE_IDLE, ACK_TIMEOUT); - -out: - return ret; -} - - -static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - int ret = 0; - struct snd_soc_codec *codec = dai->codec; - struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(codec); - - dev_dbg(codec->dev, "%s: cmd = %d\n", __func__, cmd); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - ret = tdf8532_start_play(tdf8532); - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - ret = tdf8532_stop_play(tdf8532); - break; - } - - return ret; -} - -static int tdf8532_mute(struct snd_soc_dai *dai, int mute) -{ - struct snd_soc_codec *codec = dai->codec; - struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(dai->codec); - - dev_dbg(codec->dev, "%s\n", __func__); - - if (mute) - return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, - CHNL_MASK(CHNL_MAX)); - else - return tdf8532_amp_write(tdf8532, SET_CHNL_UNMUTE, - CHNL_MASK(CHNL_MAX)); -} - -static const struct snd_soc_dai_ops tdf8532_dai_ops = { - .trigger = tdf8532_dai_trigger, - .digital_mute = tdf8532_mute, -}; - -static struct snd_soc_codec_driver soc_codec_tdf8532; - -static struct snd_soc_dai_driver tdf8532_dai[] = { - { - .name = "tdf8532-hifi", - .playback = { - .stream_name = "Playback", - .channels_min = 4, - .channels_max = 4, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &tdf8532_dai_ops, - } -}; - -static int tdf8532_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - int ret; - struct tdf8532_priv *dev_data; - struct device *dev = &(i2c->dev); - - dev_dbg(&i2c->dev, "%s\n", __func__); - - dev_data = devm_kzalloc(dev, sizeof(struct tdf8532_priv), GFP_KERNEL); - - if (!dev_data) { - ret = -ENOMEM; - goto out; - } - - dev_data->i2c = i2c; - dev_data->pkt_id = 0; - dev_data->channels = 4; - - i2c_set_clientdata(i2c, dev_data); - - ret = snd_soc_register_codec(&i2c->dev, &soc_codec_tdf8532, - tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); - if (ret != 0) { - dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); - goto out; - } - -out: - return ret; -} - -static int tdf8532_i2c_remove(struct i2c_client *i2c) -{ - snd_soc_unregister_codec(&i2c->dev); - - return 0; -} - -static const struct i2c_device_id tdf8532_i2c_id[] = { - { "tdf8532", 0 }, - { } -}; - -MODULE_DEVICE_TABLE(i2c, tdf8532_i2c_id); - -#if CONFIG_ACPI -static const struct acpi_device_id tdf8532_acpi_match[] = { - {"INT34C3", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(acpi, tdf8532_acpi_match); -#endif - -static struct i2c_driver tdf8532_i2c_driver = { - .driver = { - .name = "tdf8532-codec", - .owner = THIS_MODULE, - .acpi_match_table = ACPI_PTR(tdf8532_acpi_match), - }, - .probe = tdf8532_i2c_probe, - .remove = tdf8532_i2c_remove, - .id_table = tdf8532_i2c_id, -}; - -module_i2c_driver(tdf8532_i2c_driver); - -MODULE_DESCRIPTION("ASoC NXP Semiconductors TDF8532 driver"); -MODULE_AUTHOR("Steffen Wagner "); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h deleted file mode 100644 index 6e3f2c147eace9..00000000000000 --- a/sound/soc/codecs/tdf8532.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * tdf8532.h - Codec driver for NXP Semiconductors - * Copyright (c) 2017, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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 __TDF8532_H_ -#define __TDF8532_H_ - -#define ACK_TIMEOUT 300 - -#define CHNL_MAX 5 - -#define AMP_MOD 0x80 -#define END -1 - -#define MSG_TYPE_STX 0x02 -#define MSG_TYPE_NAK 0x15 -#define MSG_TYPE_ACK 0x6 - -#define HEADER_SIZE 3 -#define HEADER_TYPE 0 -#define HEADER_PKTID 1 -#define HEADER_LEN 2 - -/* Set commands */ -#define SET_CLK_STATE 0x1A -#define CLK_DISCONNECT 0x00 -#define CLK_CONNECT 0x01 - -#define SET_CHNL_ENABLE 0x26 -#define SET_CHNL_DISABLE 0x27 - -#define SET_CHNL_MUTE 0x42 -#define SET_CHNL_UNMUTE 0x43 - -struct header_repl { - u8 msg_type; - u8 pkt_id; - u8 len; -} __packed; - -#define GET_IDENT 0xE0 - -struct get_ident_repl { - struct header_repl header; - u8 module_id; - u8 cmd_id; - u8 type_name; - u8 hw_major; - u8 hw_minor; - u8 sw_major; - u8 sw_minor; - u8 sw_sub; -} __packed; - -#define GET_ERROR 0xE2 - -struct get_error_repl { - struct header_repl header; - u8 module_id; - u8 cmd_id; - u8 last_cmd_id; - u8 error; - u8 status; -} __packed; - -#define GET_DEV_STATUS 0x80 - -enum dev_state {STATE_BOOT, STATE_IDLE, STATE_STBY, STATE_LDAG, STATE_PLAY, - STATE_PROT, STATE_SDWN, STATE_CLFA, STATE_NONE }; - -struct get_dev_status_repl { - struct header_repl header; - u8 module_id; - u8 cmd_id; - u8 state; -} __packed; - -/* Helpers */ -#define CHNL_MASK(channels) (u8)((0x00FF << channels) >> 8) - -#define tdf8532_amp_write(dev_data, ...)\ - __tdf8532_single_write(dev_data, 0, AMP_MOD, __VA_ARGS__, END) - -struct tdf8532_priv { - struct i2c_client *i2c; - u8 channels; - u8 pkt_id; -}; - -#endif diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 44168d6f0fd541..ee9179fd4f3857 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -260,16 +260,6 @@ config SND_SOC_INTEL_BXT_PCM512x_MACH with TI PCM512x I2S audio codec. Say Y or m if you have such a device. This is a recommended option. -config SND_SOC_INTEL_BXT_TDF8532_MACH - tristate "ASoC Audio driver for BXT with TDF8532 in I2S mode" - depends on X86 && ACPI && I2C - select SND_SOC_TDF8532 - select SND_SOC_COMPRESS - help - This adds support for ASoC machine driver for Broxton IVI GP MRB platform - Say Y if you have such a device. - If unsure select "N". - endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE if SND_SOC_INTEL_SKL diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 8c7d03abeff397..f58a9bda911ddd 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,7 +7,6 @@ snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-bxt-pcm512x-objs := bxt_pcm512x.o -snd-soc-sst_bxt_tdf8532-objs := bxt_tdf8532.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -31,7 +30,6 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o -obj-$(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) += snd-soc-sst_bxt_tdf8532.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c deleted file mode 100644 index 1e0e633d493b6d..00000000000000 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Intel Broxton-P I2S Machine Driver for IVI reference platform - * Copyright (c) 2017, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - */ - -#include -#include -#include -#include -#include -#include - -static const struct snd_kcontrol_new broxton_tdf8532_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), -}; - -static const struct snd_soc_dapm_widget broxton_tdf8532_widgets[] = { - SND_SOC_DAPM_SPK("Speaker", NULL), - SND_SOC_DAPM_MIC("DiranaCp", NULL), - SND_SOC_DAPM_HP("DiranaPb", NULL), - SND_SOC_DAPM_MIC("HdmiIn", NULL), - SND_SOC_DAPM_MIC("TestPinCp", NULL), - SND_SOC_DAPM_HP("TestPinPb", NULL), - SND_SOC_DAPM_MIC("BtHfpDl", NULL), - SND_SOC_DAPM_HP("BtHfpUl", NULL), - SND_SOC_DAPM_MIC("ModemDl", NULL), - SND_SOC_DAPM_HP("ModemUl", NULL), -}; - -static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { - /* Speaker BE connections */ - { "Speaker", NULL, "ssp4 Tx"}, - { "ssp4 Tx", NULL, "codec0_out"}, - - { "dirana_in", NULL, "ssp2 Rx"}, - { "ssp2 Rx", NULL, "DiranaCp"}, - - { "dirana_aux_in", NULL, "ssp2 Rx"}, - { "ssp2 Rx", NULL, "DiranaCp"}, - - { "dirana_tuner_in", NULL, "ssp2 Rx"}, - { "ssp2 Rx", NULL, "DiranaCp"}, - - { "DiranaPb", NULL, "ssp2 Tx"}, - { "ssp2 Tx", NULL, "dirana_out"}, - - { "hdmi_ssp1_in", NULL, "ssp1 Rx"}, - { "ssp1 Rx", NULL, "HdmiIn"}, - - { "TestPin_ssp5_in", NULL, "ssp5 Rx"}, - { "ssp5 Rx", NULL, "TestPinCp"}, - - { "TestPinPb", NULL, "ssp5 Tx"}, - { "ssp5 Tx", NULL, "TestPin_ssp5_out"}, - - { "BtHfp_ssp0_in", NULL, "ssp0 Rx"}, - { "ssp0 Rx", NULL, "BtHfpDl"}, - - { "BtHfpUl", NULL, "ssp0 Tx"}, - { "ssp0 Tx", NULL, "BtHfp_ssp0_out"}, - - { "Modem_ssp3_in", NULL, "ssp3 Rx"}, - { "ssp3 Rx", NULL, "ModemDl"}, - - { "ModemUl", NULL, "ssp3 Tx"}, - { "ssp3 Tx", NULL, "Modem_ssp3_out"}, -}; - -static int bxt_tdf8532_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - - /* set SSP to 32 bit */ - snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); - - return 0; -} - -/* broxton digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link broxton_tdf8532_dais[] = { - /* Front End DAI links */ - { - .name = "Speaker Port", - .stream_name = "Speaker", - .cpu_dai_name = "Speaker Pin", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST - }, - .dpcm_playback = 1, - }, - { - .name = "Dirana Capture Port", - .stream_name = "Dirana Cp", - .cpu_dai_name = "Dirana Cp Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .init = NULL, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, - { - .name = "Dirana Playback Port", - .stream_name = "Dirana Pb", - .cpu_dai_name = "Dirana Pb Pin", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST - }, - .dpcm_playback = 1, - }, - { - .name = "TestPin Capture Port", - .stream_name = "TestPin Cp", - .cpu_dai_name = "TestPin Cp Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .init = NULL, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, - { - .name = "TestPin Playback Port", - .stream_name = "TestPin Pb", - .cpu_dai_name = "TestPin Pb Pin", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST - }, - .dpcm_playback = 1, - }, - { - .name = "BtHfp Capture Port", - .stream_name = "BtHfp Cp", - .cpu_dai_name = "BtHfp Cp Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .init = NULL, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, - { - .name = "BtHfp Playback Port", - .stream_name = "BtHfp Pb", - .cpu_dai_name = "BtHfp Pb Pin", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST - }, - .dpcm_playback = 1, - }, - { - .name = "Modem Capture Port", - .stream_name = "Modem Cp", - .cpu_dai_name = "Modem Cp Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .init = NULL, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, - { - .name = "Modem Playback Port", - .stream_name = "Modem Pb", - .cpu_dai_name = "Modem Pb Pin", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST - }, - .dpcm_playback = 1, - }, - { - .name = "HDMI Capture Port", - .stream_name = "HDMI Cp", - .cpu_dai_name = "HDMI Cp Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .init = NULL, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, - { - .name = "Dirana Aux Capture Port", - .stream_name = "Dirana Aux Cp", - .cpu_dai_name = "Dirana Aux Cp Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .init = NULL, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, - { - .name = "Dirana Tuner Capture Port", - .stream_name = "Dirana Tuner Cp", - .cpu_dai_name = "Dirana Tuner Cp Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .init = NULL, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, - /* Probe DAI links*/ - { - .name = "Bxt Compress Probe playback", - .stream_name = "Probe Playback", - .cpu_dai_name = "Compress Probe0 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .init = NULL, - .nonatomic = 1, - .dynamic = 1, - }, - { - .name = "Bxt Compress Probe capture", - .stream_name = "Probe Capture", - .cpu_dai_name = "Compress Probe1 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .init = NULL, - .nonatomic = 1, - .dynamic = 1, - }, - /* Trace Buffer DAI links */ - { - .name = "Bxt Trace Buffer0", - .stream_name = "Core 0 Trace Buffer", - .cpu_dai_name = "TraceBuffer0 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .capture_only = true, - .ignore_suspend = 1, - .dynamic = 1, - }, - { - .name = "Bxt Trace Buffer1", - .stream_name = "Core 1 Trace Buffer", - .cpu_dai_name = "TraceBuffer1 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .capture_only = true, - .ignore_suspend = 1, - .dynamic = 1, - }, - /* Back End DAI links */ - { - /* SSP0 - BT */ - .name = "SSP0-Codec", - .id = 0, - .cpu_dai_name = "SSP0 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .ignore_suspend = 1, - .dpcm_capture = 1, - .dpcm_playback = 1, - .no_pcm = 1, - }, - { - /* SSP1 - HDMI-In */ - .name = "SSP1-Codec", - .id = 1, - .cpu_dai_name = "SSP1 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .ignore_suspend = 1, - .dpcm_capture = 1, - .no_pcm = 1, - }, - { - /* SSP2 - Dirana */ - .name = "SSP2-Codec", - .id = 2, - .cpu_dai_name = "SSP2 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .ignore_suspend = 1, - .dpcm_capture = 1, - .dpcm_playback = 1, - .no_pcm = 1, - .be_hw_params_fixup = bxt_tdf8532_ssp2_fixup, - }, - { - /* SSP3 - Modem */ - .name = "SSP3-Codec", - .id = 3, - .cpu_dai_name = "SSP3 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .ignore_suspend = 1, - .dpcm_capture = 1, - .dpcm_playback = 1, - .no_pcm = 1, - }, - { - /* SSP4 - Amplifier */ - .name = "SSP4-Codec", - .id = 4, - .cpu_dai_name = "SSP4 Pin", - .codec_name = "i2c-INT34C3:00", - .codec_dai_name = "tdf8532-hifi", - .platform_name = "0000:00:0e.0", - .ignore_suspend = 1, - .dpcm_playback = 1, - .no_pcm = 1, - }, - { - /* SSP5 - TestPin */ - .name = "SSP5-Codec", - .id = 5, - .cpu_dai_name = "SSP5 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:00:0e.0", - .ignore_suspend = 1, - .dpcm_capture = 1, - .dpcm_playback = 1, - .no_pcm = 1, - }, -}; - -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) -static int bxt_add_dai_link(struct snd_soc_card *card, - struct snd_soc_dai_link *link) -{ - link->platform_name = "0000:00:0e.0"; - link->nonatomic = 1; - return 0; -} -#endif - -/* broxton audio machine driver for TDF8532 */ -static struct snd_soc_card broxton_tdf8532 = { - .name = "broxton_tdf8532", - .dai_link = broxton_tdf8532_dais, - .num_links = ARRAY_SIZE(broxton_tdf8532_dais), - .controls = broxton_tdf8532_controls, - .num_controls = ARRAY_SIZE(broxton_tdf8532_controls), - .dapm_widgets = broxton_tdf8532_widgets, - .num_dapm_widgets = ARRAY_SIZE(broxton_tdf8532_widgets), - .dapm_routes = broxton_tdf8532_map, - .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), - .fully_routed = true, -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) - .add_dai_link = bxt_add_dai_link, -#endif -}; - -static int broxton_tdf8532_audio_probe(struct platform_device *pdev) -{ - dev_info(&pdev->dev, "%s registering %s\n", __func__, pdev->name); - broxton_tdf8532.dev = &pdev->dev; - return snd_soc_register_card(&broxton_tdf8532); -} - -static int broxton_tdf8532_audio_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&broxton_tdf8532); - return 0; -} - -static struct platform_driver broxton_tdf8532_audio = { - .probe = broxton_tdf8532_audio_probe, - .remove = broxton_tdf8532_audio_remove, - .driver = { - .name = "bxt_tdf8532", - .pm = &snd_soc_pm_ops, - }, -}; - -module_platform_driver(broxton_tdf8532_audio) - -/* Module information */ -MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:gpmrb_machine"); -MODULE_ALIAS("platform:bxt_tdf8532"); From 8cd67a360253c81d8ac26ad584b783e6d306f827 Mon Sep 17 00:00:00 2001 From: "Wagner, Steffen" Date: Tue, 15 May 2018 17:14:50 +0800 Subject: [PATCH 194/285] ASoC: tdf8532: NXP TDF8532 audio class-D amplifier driver This is a basic driver to register the codec, expose the codec DAI and control the power mode of the amplifier. Change-Id: Ie6ab037cd4d6c87e8e139b6d8af6cd4295445bf2 Signed-off-by: Mohit Sinha Signed-off-by: Steffen Wagner Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/15296 Reviewed-by: B, Jayachandran Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tdf8532.c | 379 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tdf8532.h | 101 ++++++++++ 4 files changed, 487 insertions(+) create mode 100644 sound/soc/codecs/tdf8532.c create mode 100644 sound/soc/codecs/tdf8532.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9548f63ca531c8..fbacaa0a724bd4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -157,6 +157,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5720 if I2C select SND_SOC_TAS6424 if I2C select SND_SOC_TDA7419 if I2C + select SND_SOC_TDF8532 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -954,6 +955,10 @@ config SND_SOC_TDA7419 depends on I2C select REGMAP_I2C +config SND_SOC_TDF8532 + tristate + depends on I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e849d1495308f9..40480a5243ff2e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -168,6 +168,7 @@ snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o +snd-soc-tdf8532-objs := tdf8532.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -420,6 +421,7 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o +obj-$(CONFIG_SND_SOC_TDF8532) += snd-soc-tdf8532.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c new file mode 100644 index 00000000000000..59db83da37587a --- /dev/null +++ b/sound/soc/codecs/tdf8532.c @@ -0,0 +1,379 @@ +/* + * Codec driver for NXP Semiconductors - TDF8532 + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tdf8532.h" + +static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, + va_list valist, u8 *payload) +{ + int param; + u8 len; + u8 *cmd_payload; + const u8 cmd_offset = 3; + + payload[HEADER_TYPE] = MSG_TYPE_STX; + payload[HEADER_PKTID] = dev_data->pkt_id; + + cmd_payload = &(payload[cmd_offset]); + + param = va_arg(valist, int); + len = 0; + + while (param != END) { + cmd_payload[len] = param; + + len++; + + param = va_arg(valist, int); + } + + payload[HEADER_LEN] = len; + + return len + cmd_offset; +} + +static int __tdf8532_single_write(struct tdf8532_priv *dev_data, + int dummy, ...) +{ + va_list valist; + int ret; + u8 len; + u8 payload[255]; + + va_start(valist, dummy); + + len = __tdf8532_build_pkt(dev_data, valist, payload); + + va_end(valist); + + print_hex_dump_debug("tdf8532-codec: Tx:", DUMP_PREFIX_NONE, 32, 1, + payload, len, false); + ret = i2c_master_send(dev_data->i2c, payload, len); + + dev_data->pkt_id++; + + if (ret < 0) { + dev_err(&(dev_data->i2c->dev), + "i2c send packet returned: %d\n", ret); + + return ret; + } + + return 0; +} + + +static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, + unsigned long timeout) +{ + uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; + unsigned long timeout_point = jiffies + timeout; + int ret; + + do { + ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); + if (ret < 0) + goto out; + } while (time_before(jiffies, timeout_point) && + ack_repl[0] != MSG_TYPE_ACK); + + if (ack_repl[0] != MSG_TYPE_ACK) + return -ETIME; + else + return ack_repl[2]; + +out: + return ret; +} + +static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, + char **repl_buff) +{ + int ret; + uint8_t len; + + struct device *dev = &(dev_data->i2c->dev); + + ret = tdf8532_read_wait_ack(dev_data, msecs_to_jiffies(ACK_TIMEOUT)); + + if (ret < 0) { + dev_err(dev, + "Error waiting for ACK reply: %d\n", ret); + goto out; + } + + len = ret + HEADER_SIZE; + + *repl_buff = kzalloc(len, GFP_KERNEL); + + ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); + + print_hex_dump_debug("tdf8532-codec: Rx:", DUMP_PREFIX_NONE, 32, 1, + *repl_buff, len, false); + + if (ret < 0 || ret != len) { + dev_err(dev, + "i2c recv packet returned: %d (expected: %d)\n", + ret, len); + goto out_free; + } + + return len; + +out_free: + kfree(*repl_buff); + repl_buff = NULL; +out: + return ret; +} + +static int tdf8532_get_state(struct tdf8532_priv *dev_data, + struct get_dev_status_repl **status_repl) +{ + int ret = 0; + char *repl_buff = NULL; + + ret = tdf8532_amp_write(dev_data, GET_DEV_STATUS); + if (ret < 0) + goto out; + + ret = tdf8532_single_read(dev_data, &repl_buff); + if (ret < 0) + goto out; + + *status_repl = (struct get_dev_status_repl *) repl_buff; + +out: + return ret; +} + +static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, + unsigned long timeout) +{ + unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); + int ret; + struct get_dev_status_repl *status_repl; + struct device *dev = &(dev_data->i2c->dev); + + do { + ret = tdf8532_get_state(dev_data, &status_repl); + if (ret < 0) + goto out; + + print_hex_dump_debug("tdf8532-codec: wait_state: ", + DUMP_PREFIX_NONE, 32, 1, status_repl, + 6, false); + } while (time_before(jiffies, timeout_point) + && status_repl->state != req_state); + + if (status_repl->state == req_state) + return 0; + + ret = -ETIME; + + dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", + status_repl->state, req_state, ret); + +out: + kfree(status_repl); + return ret; +} + +static int tdf8532_start_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_CONNECT); + if (ret < 0) + return ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, + CHNL_MASK(tdf8532->channels)); + + if (ret >= 0) + ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); + + return ret; +} + + +static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, + CHNL_MASK(tdf8532->channels)); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); + if (ret < 0) + goto out; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_IDLE, ACK_TIMEOUT); + +out: + return ret; +} + + +static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct snd_soc_codec *codec = dai->codec; + struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: cmd = %d\n", __func__, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = tdf8532_start_play(tdf8532); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = tdf8532_stop_play(tdf8532); + break; + } + + return ret; +} + +static int tdf8532_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(codec->dev, "%s\n", __func__); + + if (mute) + return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, + CHNL_MASK(CHNL_MAX)); + else + return tdf8532_amp_write(tdf8532, SET_CHNL_UNMUTE, + CHNL_MASK(CHNL_MAX)); +} + +static const struct snd_soc_dai_ops tdf8532_dai_ops = { + .trigger = tdf8532_dai_trigger, + .digital_mute = tdf8532_mute, +}; + +static struct snd_soc_codec_driver soc_codec_tdf8532; + +static struct snd_soc_dai_driver tdf8532_dai[] = { + { + .name = "tdf8532-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 4, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tdf8532_dai_ops, + } +}; + +static int tdf8532_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret; + struct tdf8532_priv *dev_data; + struct device *dev = &(i2c->dev); + + dev_dbg(&i2c->dev, "%s\n", __func__); + + dev_data = devm_kzalloc(dev, sizeof(struct tdf8532_priv), GFP_KERNEL); + + if (!dev_data) { + ret = -ENOMEM; + goto out; + } + + if (ret < 0) + dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); + + dev_data->i2c = i2c; + dev_data->pkt_id = 0; + dev_data->channels = 4; + + i2c_set_clientdata(i2c, dev_data); + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_tdf8532, + tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + goto out; + } + +out: + return ret; +} + +static int tdf8532_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +static const struct i2c_device_id tdf8532_i2c_id[] = { + { "tdf8532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tdf8532_i2c_id); + +#if CONFIG_ACPI +static const struct acpi_device_id tdf8532_acpi_match[] = { + {"INT34C3", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tdf8532_acpi_match); +#endif + +static struct i2c_driver tdf8532_i2c_driver = { + .driver = { + .name = "tdf8532-codec", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(tdf8532_acpi_match), + }, + .probe = tdf8532_i2c_probe, + .remove = tdf8532_i2c_remove, + .id_table = tdf8532_i2c_id, +}; + +module_i2c_driver(tdf8532_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP Semiconductors TDF8532 driver"); +MODULE_AUTHOR("Steffen Wagner "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h new file mode 100644 index 00000000000000..6e3f2c147eace9 --- /dev/null +++ b/sound/soc/codecs/tdf8532.h @@ -0,0 +1,101 @@ +/* + * tdf8532.h - Codec driver for NXP Semiconductors + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 __TDF8532_H_ +#define __TDF8532_H_ + +#define ACK_TIMEOUT 300 + +#define CHNL_MAX 5 + +#define AMP_MOD 0x80 +#define END -1 + +#define MSG_TYPE_STX 0x02 +#define MSG_TYPE_NAK 0x15 +#define MSG_TYPE_ACK 0x6 + +#define HEADER_SIZE 3 +#define HEADER_TYPE 0 +#define HEADER_PKTID 1 +#define HEADER_LEN 2 + +/* Set commands */ +#define SET_CLK_STATE 0x1A +#define CLK_DISCONNECT 0x00 +#define CLK_CONNECT 0x01 + +#define SET_CHNL_ENABLE 0x26 +#define SET_CHNL_DISABLE 0x27 + +#define SET_CHNL_MUTE 0x42 +#define SET_CHNL_UNMUTE 0x43 + +struct header_repl { + u8 msg_type; + u8 pkt_id; + u8 len; +} __packed; + +#define GET_IDENT 0xE0 + +struct get_ident_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 type_name; + u8 hw_major; + u8 hw_minor; + u8 sw_major; + u8 sw_minor; + u8 sw_sub; +} __packed; + +#define GET_ERROR 0xE2 + +struct get_error_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 last_cmd_id; + u8 error; + u8 status; +} __packed; + +#define GET_DEV_STATUS 0x80 + +enum dev_state {STATE_BOOT, STATE_IDLE, STATE_STBY, STATE_LDAG, STATE_PLAY, + STATE_PROT, STATE_SDWN, STATE_CLFA, STATE_NONE }; + +struct get_dev_status_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 state; +} __packed; + +/* Helpers */ +#define CHNL_MASK(channels) (u8)((0x00FF << channels) >> 8) + +#define tdf8532_amp_write(dev_data, ...)\ + __tdf8532_single_write(dev_data, 0, AMP_MOD, __VA_ARGS__, END) + +struct tdf8532_priv { + struct i2c_client *i2c; + u8 channels; + u8 pkt_id; +}; + +#endif From e624e8a382e97c3ade5b62ec9a2844859182c950 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 May 2018 17:14:51 +0800 Subject: [PATCH 195/285] ASoC: tdf8532: Fix compilation warnings Initialized the reported variables, listed below warning: 'ret' may be used uninitialized in this function warning: 'status_repl' may be used uninitialized in this function Change-Id: I6ca5a6e017402a582239d75959c122ffaa9f7298 Signed-off-by: Gogineni, GiribabuX Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/17572 Reviewed-by: Singh, Guneshwor O Reviewed-by: Sinha, Mohit Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 59db83da37587a..5f072079fbb76b 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -172,7 +172,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, { unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; - struct get_dev_status_repl *status_repl; + struct get_dev_status_repl *status_repl = NULL; struct device *dev = &(dev_data->i2c->dev); do { @@ -318,9 +318,6 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, goto out; } - if (ret < 0) - dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); - dev_data->i2c = i2c; dev_data->pkt_id = 0; dev_data->channels = 4; From 9debbe80b0e420843291b048d300ff4ba48c81e9 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 May 2018 17:14:52 +0800 Subject: [PATCH 196/285] ASoC: tdf8532: Add delay while reading a packet from I2C While doing the continuous play and stop, the codec may not be ready for I2C reading after successive writes. This triggers BE failure, because I2C reading value is incorrect. Fix this by adding 10ms delay to ensure the smooth I2C read and write. Change-Id: If918e263bc799fecc2c807229f5b4b165e011fa6 Signed-off-by: Gogineni, GiribabuX Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/20404 Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Nc, Shreyas Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 5f072079fbb76b..1860e8264ebab8 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -90,6 +90,7 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, unsigned long timeout_point = jiffies + timeout; int ret; + usleep_range(10000,20000); do { ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); if (ret < 0) From 50eba5f06fc6346b2ce24a5521fd52c24f5ab6c7 Mon Sep 17 00:00:00 2001 From: Liu Changcheng Date: Fri, 11 May 2018 17:24:01 +0800 Subject: [PATCH 197/285] ASoC: tdf8532: fix memleak in tdf8532_wait_state Fix kmemleak issue in tdf8532_wait_state function by releasing the memory getting allocated continuosly in instance of get_dev_status_repl i.e. status_repl before exiting the function. kernel memory leakage in audio stack/kmemleak backtrace: unreferenced object 0xffff88006227cc20 (size 32): comm "irq/25-snd_soc_", pid 2302, jiffies 4294679082 (age 5506.010s) hex dump (first 32 bytes): 02 00 03 80 80 02 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x4a/0xa0 [] __kmalloc+0x128/0x210 [] tdf8532_wait_state.constprop.5+0x116/0x260 [snd_soc_tdf8532] [] tdf8532_dai_trigger+0xab/0x15a [snd_soc_tdf8532] [] soc_pcm_trigger+0x75/0x130 [] dpcm_do_trigger.isra.6+0x29/0x90 [] dpcm_be_dai_trigger+0x18d/0x350 Change-Id: I550897d6b1efbd5ebbe15ab47038adf99581a82f Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62665 Signed-off-by: Liu Changcheng Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/23270 Reviewed-by: Shaik, ShahinaX Reviewed-by: Singh, Guneshwor O Reviewed-by: Gogineni, GiribabuX Reviewed-by: Tewani, Pradeep D Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 1860e8264ebab8..aaf7254672cbeb 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -174,29 +174,31 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; struct get_dev_status_repl *status_repl = NULL; + u8 cur_state = STATE_NONE; struct device *dev = &(dev_data->i2c->dev); do { ret = tdf8532_get_state(dev_data, &status_repl); if (ret < 0) goto out; - + cur_state = status_repl->state; print_hex_dump_debug("tdf8532-codec: wait_state: ", DUMP_PREFIX_NONE, 32, 1, status_repl, 6, false); + + kfree(status_repl); + status_repl = NULL; } while (time_before(jiffies, timeout_point) - && status_repl->state != req_state); + && cur_state != req_state); - if (status_repl->state == req_state) + if (cur_state == req_state) return 0; +out: ret = -ETIME; dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", - status_repl->state, req_state, ret); - -out: - kfree(status_repl); + cur_state, req_state, ret); return ret; } From 489541e63cb0766221bd402710ac4b8d3c5dd84b Mon Sep 17 00:00:00 2001 From: Liu Changcheng Date: Fri, 11 May 2018 17:11:42 +0800 Subject: [PATCH 198/285] ASoC: tdf8532: right free allocated space in case of error 1. Check allocated space before using it. 2. The repl_buff parameter in tdf8523_single_read is used to store the read data from i2c interface. When the data isn't right read, the pre-allocate space should be freed and the content of repl_buff should be set as NULL in case of being wrong used by the caller. 3. In the wrong case i.e. ret != len, return -EINVAL Change-Id: I3d0e12a9fcb6516716efc92eb734a0248ab3fb28 Tracked-On: https://jira01.devtools.intel.com/browse/OAM-62665 Signed-off-by: Liu Changcheng Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/23266 Reviewed-by: Shaik, ShahinaX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Gogineni, GiribabuX Reviewed-by: Singh, Guneshwor O Reviewed-by: Tewani, Pradeep D Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index aaf7254672cbeb..685a20d98b6b6a 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -107,11 +107,11 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, return ret; } -static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, +static int tdf8532_single_read(struct tdf8532_priv *dev_data, char **repl_buff) { int ret; - uint8_t len; + int len; struct device *dev = &(dev_data->i2c->dev); @@ -126,6 +126,10 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, len = ret + HEADER_SIZE; *repl_buff = kzalloc(len, GFP_KERNEL); + if (*repl_buff == NULL) { + ret = -ENOMEM; + goto out; + } ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); @@ -136,6 +140,8 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, dev_err(dev, "i2c recv packet returned: %d (expected: %d)\n", ret, len); + + ret = -EINVAL; goto out_free; } @@ -143,7 +149,7 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, out_free: kfree(*repl_buff); - repl_buff = NULL; + *repl_buff = NULL; out: return ret; } From 4635f99b013c94efe2bed9ff36ad56982b63b201 Mon Sep 17 00:00:00 2001 From: Wu Zhigang Date: Tue, 15 May 2018 17:14:53 +0800 Subject: [PATCH 199/285] ASoC: tdf8532: Fix the codec status error issue on APL-GPMRB Based on the TDF8532 manual: If the wait_state result is ok, we should send CLK_DISCONNECT command to force codec from STANDBY(2) to IDLE(1). If the wait_state result is timeout, the codec state should be at Clockfail(7), we still should send CLK_DISCONNECT command force the codec from Clockfail(7) to Idle(1). Signed-off-by: Wu Zhigang Reviewed-by: Keyon Jie --- sound/soc/codecs/tdf8532.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 685a20d98b6b6a..0ef8fe406270a5 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -236,8 +236,14 @@ static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) goto out; ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); - if (ret < 0) - goto out; + + /* Refer to TDF8532 manual: + * If the wait_state result is ok, we should send CLK_DISCONNECT + * command to force codec from STANDBY(2) to IDLE(1). + * If the wait_state result is timeout, the codec state should be + * at Clockfail(7), we still should send CLK_DISCONNECT command + * force the codec from Clockfail(7) to Idle(1). + */ ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); if (ret < 0) From c084aa7adb25e74e00b43660e61c571bd81aef66 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 23 Jun 2018 08:45:49 -0500 Subject: [PATCH 200/285] ASoC: codecs: tdf8532: move to component model Needs to be squashed for 4.18+ Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/tdf8532.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 0ef8fe406270a5..8a5928a894ca24 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -260,10 +260,10 @@ static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { int ret = 0; - struct snd_soc_codec *codec = dai->codec; - struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); - dev_dbg(codec->dev, "%s: cmd = %d\n", __func__, cmd); + dev_dbg(component->dev, "%s: cmd = %d\n", __func__, cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -283,10 +283,10 @@ static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, static int tdf8532_mute(struct snd_soc_dai *dai, int mute) { - struct snd_soc_codec *codec = dai->codec; - struct tdf8532_priv *tdf8532 = snd_soc_codec_get_drvdata(dai->codec); + struct snd_soc_component *component = dai->component; + struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); - dev_dbg(codec->dev, "%s\n", __func__); + dev_dbg(component->dev, "%s\n", __func__); if (mute) return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, @@ -301,7 +301,7 @@ static const struct snd_soc_dai_ops tdf8532_dai_ops = { .digital_mute = tdf8532_mute, }; -static struct snd_soc_codec_driver soc_codec_tdf8532; +static struct snd_soc_component_driver soc_component_tdf8532; static struct snd_soc_dai_driver tdf8532_dai[] = { { @@ -339,7 +339,7 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, dev_data); - ret = snd_soc_register_codec(&i2c->dev, &soc_codec_tdf8532, + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_tdf8532, tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); if (ret != 0) { dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); @@ -352,8 +352,6 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, static int tdf8532_i2c_remove(struct i2c_client *i2c) { - snd_soc_unregister_codec(&i2c->dev); - return 0; } From 3f3d23cc7c1d275fbdc483867291157582094bff Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 22 May 2018 18:23:43 +0800 Subject: [PATCH 201/285] ASoC: Intel: Boards: Add BXTP MRB machine driver for NXP TDF8532 This is the machine driver for NXP TDF8532 Change-Id: Ieee7ba1fc2dab6fbe43836b65def88c81360d48f Signed-off-by: Mohit Sinha Signed-off-by: Markus Schweikhardt Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/15375 Reviewed-by: Shaik, Kareem M Reviewed-by: B, Jayachandran Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/Kconfig | 10 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_tdf8532.c | 209 +++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_tdf8532.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index ee9179fd4f3857..44168d6f0fd541 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -260,6 +260,16 @@ config SND_SOC_INTEL_BXT_PCM512x_MACH with TI PCM512x I2S audio codec. Say Y or m if you have such a device. This is a recommended option. +config SND_SOC_INTEL_BXT_TDF8532_MACH + tristate "ASoC Audio driver for BXT with TDF8532 in I2S mode" + depends on X86 && ACPI && I2C + select SND_SOC_TDF8532 + select SND_SOC_COMPRESS + help + This adds support for ASoC machine driver for Broxton IVI GP MRB platform + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE if SND_SOC_INTEL_SKL diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index f58a9bda911ddd..8c7d03abeff397 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,7 @@ snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-bxt-pcm512x-objs := bxt_pcm512x.o +snd-soc-sst_bxt_tdf8532-objs := bxt_tdf8532.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -30,6 +31,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) += snd-soc-sst_bxt_tdf8532.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c new file mode 100644 index 00000000000000..027060b17322a3 --- /dev/null +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -0,0 +1,209 @@ +/* + * Intel Broxton-P I2S Machine Driver for IVI reference platform + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include + +static const struct snd_kcontrol_new broxton_tdf8532_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static const struct snd_soc_dapm_widget broxton_tdf8532_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DiranaCp", NULL), + SND_SOC_DAPM_HP("DiranaPb", NULL), + SND_SOC_DAPM_MIC("HdmiIn", NULL), + SND_SOC_DAPM_MIC("TestPinCp", NULL), + SND_SOC_DAPM_HP("TestPinPb", NULL), + SND_SOC_DAPM_MIC("BtHfpDl", NULL), + SND_SOC_DAPM_HP("BtHfpUl", NULL), + SND_SOC_DAPM_MIC("ModemDl", NULL), + SND_SOC_DAPM_HP("ModemUl", NULL), +}; + +static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { + + /* Speaker BE connections */ + { "Speaker", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + + { "dirana_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_aux_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_tuner_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "DiranaPb", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "dirana_out"}, + + { "hdmi_ssp1_in", NULL, "ssp1 Rx"}, + { "ssp1 Rx", NULL, "HdmiIn"}, + + { "TestPin_ssp5_in", NULL, "ssp5 Rx"}, + { "ssp5 Rx", NULL, "TestPinCp"}, + + { "TestPinPb", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "TestPin_ssp5_out"}, + + { "BtHfp_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "BtHfpDl"}, + + { "BtHfpUl", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "BtHfp_ssp0_out"}, + + { "Modem_ssp3_in", NULL, "ssp3 Rx"}, + { "ssp3 Rx", NULL, "ModemDl"}, + + { "ModemUl", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "Modem_ssp3_out"}, +}; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Back End DAI links */ + { + /* SSP0 - BT */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP1 - HDMI-In */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + /* SSP2 - Dirana */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP3 - Modem */ + .name = "SSP3-Codec", + .id = 3, + .cpu_dai_name = "SSP3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP4 - Amplifier */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .codec_name = "i2c-INT34C3:00", + .codec_dai_name = "tdf8532-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP5 - TestPin */ + .name = "SSP5-Codec", + .id = 5, + .cpu_dai_name = "SSP5 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int bxt_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:00:0e.0"; + link->nonatomic = 1; + return 0; +} + +/* broxton audio machine driver for TDF8532 */ +static struct snd_soc_card broxton_tdf8532 = { + .name = "broxton_tdf8532", + .dai_link = broxton_tdf8532_dais, + .num_links = ARRAY_SIZE(broxton_tdf8532_dais), + .controls = broxton_tdf8532_controls, + .num_controls = ARRAY_SIZE(broxton_tdf8532_controls), + .dapm_widgets = broxton_tdf8532_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_tdf8532_widgets), + .dapm_routes = broxton_tdf8532_map, + .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_tdf8532_audio_probe(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s registering %s\n", __func__, pdev->name); + broxton_tdf8532.dev = &pdev->dev; + return snd_soc_register_card(&broxton_tdf8532); +} + +static int broxton_tdf8532_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_tdf8532); + return 0; +} + +static struct platform_driver broxton_tdf8532_audio = { + .probe = broxton_tdf8532_audio_probe, + .remove = broxton_tdf8532_audio_remove, + .driver = { + .name = "bxt_tdf8532", + }, +}; + +module_platform_driver(broxton_tdf8532_audio) + +/* Module information */ +MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gpmrb_machine"); From 273dca2278f1deaf069efce5f4762960353cb278 Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 6 Jul 2017 16:10:32 +0530 Subject: [PATCH 202/285] ASoC: Intel: Board: DAI links for probe in GPMRB machine driver Added two DAI links for probe playback and capture Change-Id: I0bf364eba3b6a2b779625a6fd1b664c2530a1ab2 Signed-off-by: Sinha, Mohit --- sound/soc/intel/boards/bxt_tdf8532.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 027060b17322a3..1e2b8be0012707 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -78,6 +78,27 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Probe DAI links*/ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, /* Back End DAI links */ { /* SSP0 - BT */ From c0ae9de992a1262960c721ce9c864d6a5f856abf Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 6 Jul 2017 16:21:19 +0530 Subject: [PATCH 203/285] ASoC: Intel: Boards: Add FW logging DAI-links for GPMRB Add two FW logging DAI for each DSP core Change-Id: Ic825ecb4afbbcacabda6b74e2e5f2969fc722a1f Signed-off-by: Sinha, Mohit --- sound/soc/intel/boards/bxt_tdf8532.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 1e2b8be0012707..325b59adaf1c05 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -99,6 +99,27 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .init = NULL, .nonatomic = 1, }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, /* Back End DAI links */ { /* SSP0 - BT */ From 8c9ed3e3d975a44c8fc4151f668d3c96d5c28506 Mon Sep 17 00:00:00 2001 From: "Kareem,Shaik" Date: Wed, 30 Aug 2017 16:46:40 +0530 Subject: [PATCH 204/285] ASoC: Intel: Board: Add pm_ops to fix suspend/resume issue Audio playback not resumed after it is suspended. Add snd_soc_pm_ops to execute power management operation. Change-Id: I84ccf6a0ac7e35c1f79971ee59555f24024d4309 Signed-off-by: Mohit Sinha Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/17914 Reviewed-by: Shaik, Kareem M Reviewed-by: Prusty, Subhransu S Reviewed-by: H S, Vijay Reviewed-by: Kp, Jeeja Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/bxt_tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 325b59adaf1c05..c7b7fe3f9ed7c4 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -240,6 +240,7 @@ static struct platform_driver broxton_tdf8532_audio = { .remove = broxton_tdf8532_audio_remove, .driver = { .name = "bxt_tdf8532", + .pm = &snd_soc_pm_ops, }, }; From 50bb756f8e051c79cf5e64fa75c534eae4c134c7 Mon Sep 17 00:00:00 2001 From: Mohit Sinha Date: Mon, 4 Sep 2017 23:31:17 +0530 Subject: [PATCH 205/285] ASoC: Intel: Board: Add fixup for 32 bit masking Fixup function does the masking of the format to set the SSP2 to 32 bit Change-Id: I1c5f20ce1244f9c3a47a47342d46184fdd718290 Signed-off-by: Mohit Sinha Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/18076 Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/bxt_tdf8532.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index c7b7fe3f9ed7c4..27361e8f72d3f0 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -76,6 +76,18 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { { "ssp3 Tx", NULL, "Modem_ssp3_out"}, }; +static int bxt_tdf8532_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* set SSP to 32 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + + return 0; +} + /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { /* Probe DAI links*/ @@ -158,6 +170,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .dpcm_capture = 1, .dpcm_playback = 1, .no_pcm = 1, + .be_hw_params_fixup = bxt_tdf8532_ssp2_fixup, }, { /* SSP3 - Modem */ From 6f7dfb396d8b62bf764b3604b028e22e9e3d3965 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 22 May 2018 18:23:44 +0800 Subject: [PATCH 206/285] ASoC: Intel: bxt-tdf8532: reuse machine driver for GP-MRB Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 27361e8f72d3f0..4dd73d356740f4 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -263,3 +263,4 @@ module_platform_driver(broxton_tdf8532_audio) MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:gpmrb_machine"); +MODULE_ALIAS("platform:bxt_tdf8532"); From 4f9f57f2107cc4b1a0b089c446fcf3577b25ceef Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 28 May 2018 13:43:18 +0800 Subject: [PATCH 207/285] ASoC: Intel: bxt-tdf8532: FIX: don't use add_dai_link() for SOF We set ignore_machine for SOF soc platform driver, which will trigger overriding FEs in soc_check_tplg_fes(), but this overriding may be overidden again by add_dai_link() in bxt_tdf8532, e.g. platform_name will be modified to be "0000:00:0e.0", which is not exist in SOF. Here add #ifdef to bypass add_dai_link() for using the machine driver with SOF to fix the overridden again issue. Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 4dd73d356740f4..dd8bdfd352b2d1 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -212,6 +212,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { }, }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) { @@ -219,6 +220,7 @@ static int bxt_add_dai_link(struct snd_soc_card *card, link->nonatomic = 1; return 0; } +#endif /* broxton audio machine driver for TDF8532 */ static struct snd_soc_card broxton_tdf8532 = { @@ -232,7 +234,9 @@ static struct snd_soc_card broxton_tdf8532 = { .dapm_routes = broxton_tdf8532_map, .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), .fully_routed = true, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) .add_dai_link = bxt_add_dai_link, +#endif }; static int broxton_tdf8532_audio_probe(struct platform_device *pdev) From 6d53213a698faa9510855bc9fc1d8254b55b351a Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Wed, 20 Jun 2018 16:50:51 +0800 Subject: [PATCH 208/285] ASoC: Intel: bxt-tdf8532: change probe and trace buffer dai_links to dynamic We should use .dynamic for all FE dai_links, so change probe and trace buffer ones here to align to that. Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index dd8bdfd352b2d1..abdc04e389a8fb 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -100,6 +100,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .init = NULL, .nonatomic = 1, + .dynamic = 1, }, { .name = "Bxt Compress Probe capture", @@ -110,6 +111,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .init = NULL, .nonatomic = 1, + .dynamic = 1, }, /* Trace Buffer DAI links */ { @@ -121,6 +123,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .capture_only = true, .ignore_suspend = 1, + .dynamic = 1, }, { .name = "Bxt Trace Buffer1", @@ -131,6 +134,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .platform_name = "0000:00:0e.0", .capture_only = true, .ignore_suspend = 1, + .dynamic = 1, }, /* Back End DAI links */ { From 57471607ad8084f733f62192723a64b257861850 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 13:31:56 -0500 Subject: [PATCH 209/285] ASoC: codecs: TDF8532: use Linux style Make checkpatch happy, no functionality change Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/tdf8532.c | 82 ++++++++++++++++---------------------- sound/soc/codecs/tdf8532.h | 13 +----- 2 files changed, 37 insertions(+), 58 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 8a5928a894ca24..d8ae9ff441c8d9 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Codec driver for NXP Semiconductors - TDF8532 * Copyright (c) 2017, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. */ #include @@ -24,7 +16,7 @@ #include "tdf8532.h" static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, - va_list valist, u8 *payload) + va_list valist, u8 *payload) { int param; u8 len; @@ -34,7 +26,7 @@ static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, payload[HEADER_TYPE] = MSG_TYPE_STX; payload[HEADER_PKTID] = dev_data->pkt_id; - cmd_payload = &(payload[cmd_offset]); + cmd_payload = &payload[cmd_offset]; param = va_arg(valist, int); len = 0; @@ -53,7 +45,7 @@ static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, } static int __tdf8532_single_write(struct tdf8532_priv *dev_data, - int dummy, ...) + int dummy, ...) { va_list valist; int ret; @@ -67,14 +59,14 @@ static int __tdf8532_single_write(struct tdf8532_priv *dev_data, va_end(valist); print_hex_dump_debug("tdf8532-codec: Tx:", DUMP_PREFIX_NONE, 32, 1, - payload, len, false); + payload, len, false); ret = i2c_master_send(dev_data->i2c, payload, len); dev_data->pkt_id++; if (ret < 0) { - dev_err(&(dev_data->i2c->dev), - "i2c send packet returned: %d\n", ret); + dev_err(&dev_data->i2c->dev, + "i2c send packet returned: %d\n", ret); return ret; } @@ -82,15 +74,14 @@ static int __tdf8532_single_write(struct tdf8532_priv *dev_data, return 0; } - -static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, - unsigned long timeout) +static u8 tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, + unsigned long timeout) { - uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; + u8 ack_repl[HEADER_SIZE] = {0, 0, 0}; unsigned long timeout_point = jiffies + timeout; int ret; - usleep_range(10000,20000); + usleep_range(10000, 20000); do { ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); if (ret < 0) @@ -108,18 +99,18 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, } static int tdf8532_single_read(struct tdf8532_priv *dev_data, - char **repl_buff) + char **repl_buff) { int ret; int len; - struct device *dev = &(dev_data->i2c->dev); + struct device *dev = &dev_data->i2c->dev; ret = tdf8532_read_wait_ack(dev_data, msecs_to_jiffies(ACK_TIMEOUT)); if (ret < 0) { dev_err(dev, - "Error waiting for ACK reply: %d\n", ret); + "Error waiting for ACK reply: %d\n", ret); goto out; } @@ -134,12 +125,12 @@ static int tdf8532_single_read(struct tdf8532_priv *dev_data, ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); print_hex_dump_debug("tdf8532-codec: Rx:", DUMP_PREFIX_NONE, 32, 1, - *repl_buff, len, false); + *repl_buff, len, false); if (ret < 0 || ret != len) { dev_err(dev, - "i2c recv packet returned: %d (expected: %d)\n", - ret, len); + "i2c recv packet returned: %d (expected: %d)\n", + ret, len); ret = -EINVAL; goto out_free; @@ -155,10 +146,10 @@ static int tdf8532_single_read(struct tdf8532_priv *dev_data, } static int tdf8532_get_state(struct tdf8532_priv *dev_data, - struct get_dev_status_repl **status_repl) + struct get_dev_status_repl **status_repl) { - int ret = 0; char *repl_buff = NULL; + int ret = 0; ret = tdf8532_amp_write(dev_data, GET_DEV_STATUS); if (ret < 0) @@ -168,20 +159,20 @@ static int tdf8532_get_state(struct tdf8532_priv *dev_data, if (ret < 0) goto out; - *status_repl = (struct get_dev_status_repl *) repl_buff; + *status_repl = (struct get_dev_status_repl *)repl_buff; out: return ret; } static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, - unsigned long timeout) + unsigned long timeout) { unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); - int ret; + struct device *dev = &dev_data->i2c->dev; struct get_dev_status_repl *status_repl = NULL; u8 cur_state = STATE_NONE; - struct device *dev = &(dev_data->i2c->dev); + int ret; do { ret = tdf8532_get_state(dev_data, &status_repl); @@ -189,13 +180,12 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, goto out; cur_state = status_repl->state; print_hex_dump_debug("tdf8532-codec: wait_state: ", - DUMP_PREFIX_NONE, 32, 1, status_repl, - 6, false); + DUMP_PREFIX_NONE, 32, 1, status_repl, + 6, false); kfree(status_repl); status_repl = NULL; - } while (time_before(jiffies, timeout_point) - && cur_state != req_state); + } while (time_before(jiffies, timeout_point) && cur_state != req_state); if (cur_state == req_state) return 0; @@ -204,7 +194,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, ret = -ETIME; dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", - cur_state, req_state, ret); + cur_state, req_state, ret); return ret; } @@ -217,7 +207,7 @@ static int tdf8532_start_play(struct tdf8532_priv *tdf8532) return ret; ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, - CHNL_MASK(tdf8532->channels)); + CHNL_MASK(tdf8532->channels)); if (ret >= 0) ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); @@ -225,13 +215,12 @@ static int tdf8532_start_play(struct tdf8532_priv *tdf8532) return ret; } - static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) { int ret; ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, - CHNL_MASK(tdf8532->channels)); + CHNL_MASK(tdf8532->channels)); if (ret < 0) goto out; @@ -255,13 +244,12 @@ static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) return ret; } - static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { - int ret = 0; struct snd_soc_component *component = dai->component; struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); + int ret = 0; dev_dbg(component->dev, "%s: cmd = %d\n", __func__, cmd); @@ -301,7 +289,7 @@ static const struct snd_soc_dai_ops tdf8532_dai_ops = { .digital_mute = tdf8532_mute, }; -static struct snd_soc_component_driver soc_component_tdf8532; +static const struct snd_soc_component_driver soc_component_tdf8532; static struct snd_soc_dai_driver tdf8532_dai[] = { { @@ -318,11 +306,11 @@ static struct snd_soc_dai_driver tdf8532_dai[] = { }; static int tdf8532_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { int ret; struct tdf8532_priv *dev_data; - struct device *dev = &(i2c->dev); + struct device *dev = &i2c->dev; dev_dbg(&i2c->dev, "%s\n", __func__); @@ -340,7 +328,7 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, dev_data); ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_tdf8532, - tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); + tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); if (ret != 0) { dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); goto out; diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h index 6e3f2c147eace9..8818252dcda6fa 100644 --- a/sound/soc/codecs/tdf8532.h +++ b/sound/soc/codecs/tdf8532.h @@ -1,18 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * tdf8532.h - Codec driver for NXP Semiconductors * Copyright (c) 2017, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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 __TDF8532_H_ #define __TDF8532_H_ @@ -87,7 +78,7 @@ struct get_dev_status_repl { } __packed; /* Helpers */ -#define CHNL_MASK(channels) (u8)((0x00FF << channels) >> 8) +#define CHNL_MASK(channels) (u8)((0x00FF << (channels)) >> 8) #define tdf8532_amp_write(dev_data, ...)\ __tdf8532_single_write(dev_data, 0, AMP_MOD, __VA_ARGS__, END) From c3fe5975ea88fa1f1adf3e127ef11ce233d6e390 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 13:38:08 -0500 Subject: [PATCH 210/285] ASoC: Intel: bxt-tdf8532: use Linux style Make checkpatch happy, no functionality change Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/bxt_tdf8532.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index abdc04e389a8fb..336205309e2c8b 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Broxton-P I2S Machine Driver for IVI reference platform * Copyright (c) 2017, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. */ #include @@ -37,7 +29,6 @@ static const struct snd_soc_dapm_widget broxton_tdf8532_widgets[] = { }; static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { - /* Speaker BE connections */ { "Speaker", NULL, "ssp4 Tx"}, { "ssp4 Tx", NULL, "codec0_out"}, @@ -77,7 +68,7 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { }; static int bxt_tdf8532_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); @@ -218,7 +209,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { #if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int bxt_add_dai_link(struct snd_soc_card *card, - struct snd_soc_dai_link *link) + struct snd_soc_dai_link *link) { link->platform_name = "0000:00:0e.0"; link->nonatomic = 1; From 6c732055650cf25b700d92452467ab7475498ecc Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 14:02:30 -0500 Subject: [PATCH 211/285] Revert "ASoC: Intel: replace snd_soc_codec to snd_soc_component in cnl_rt274" This reverts commit 51f6db7f214345ab933ce0ce965d40fd45f77fbc. --- sound/soc/intel/boards/cnl_rt274.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 16daa31b8d2ca0..d31af7dfc1cc59 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -111,9 +111,9 @@ static struct snd_soc_jack cnl_headset; static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) { int ret; + struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; - struct snd_soc_component *component = codec_dai->component; ret = snd_soc_card_jack_new(runtime->card, "Headset", SND_JACK_HEADSET, &cnl_headset, @@ -123,7 +123,7 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - snd_soc_component_set_jack(component, &cnl_headset, NULL); + snd_soc_codec_set_jack(codec, &cnl_headset, NULL); /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); From ab2b17d6494e1e72d3dab02631bc95fa92360a13 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 14:02:44 -0500 Subject: [PATCH 212/285] Revert "ASoC: Intel: make cnl_rt274 work with SOF" This reverts commit 1ac86e18ef5fa4a1d55d00413d86b12c740ad3b9. --- sound/soc/intel/boards/cnl_rt274.c | 49 ++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index d31af7dfc1cc59..6f8be5a4d508aa 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -29,7 +29,7 @@ #define RT274_CODEC_DAI "rt274-aif1" static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) + struct snd_kcontrol *k, int event) { struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; @@ -42,8 +42,8 @@ static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, return -EINVAL; ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL1, - CNL_FREQ_OUT, - SND_SOC_CLOCK_IN); + CNL_FREQ_OUT, + SND_SOC_CLOCK_IN); if (ret) { dev_err(codec_dai->dev, "failed to enable PLL1: %d\n", ret); @@ -76,8 +76,8 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, - cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_pcm_stream dai_params_codec = { @@ -116,9 +116,8 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dai *codec_dai = runtime->codec_dai; ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET, &cnl_headset, - cnl_headset_pins, - ARRAY_SIZE(cnl_headset_pins)); + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); if (ret) return ret; @@ -138,45 +137,42 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) } static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = CNL_BE_FIXUP_RATE; - rate->max = CNL_BE_FIXUP_RATE; - channels->min = 2; - channels->max = 2; + rate->min = rate->max = CNL_BE_FIXUP_RATE; + channels->min = channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - SNDRV_PCM_FORMAT_S24_LE); + SNDRV_PCM_FORMAT_S24_LE); return 0; } -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = 2; - channels->max = 2; + channels->min = channels->max = 2; return 0; } -#endif +static const char pname[] = "0000:00:1f.3"; static const char cname[] = "i2c-INT34C2:00"; -static struct snd_soc_dai_link cnl_rt274_dailink[] = { +struct snd_soc_dai_link cnl_rt274_dailink[] = { { .name = "SSP0-Codec", .cpu_dai_name = "SSP0 Pin", .codec_name = cname, .codec_dai_name = "rt274-aif1", + .platform_name = pname, .be_hw_params_fixup = cnl_be_fixup, .ignore_pmdown_time = 1, .no_pcm = 1, @@ -186,20 +182,28 @@ static struct snd_soc_dai_link cnl_rt274_dailink[] = { .dpcm_capture = 1, .init = cnl_rt274_init, }, -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) { .name = "dmic01", .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", + .platform_name = pname, .ignore_suspend = 1, .no_pcm = 1, .dpcm_capture = 1, .be_hw_params_fixup = cnl_dmic_fixup, }, -#endif }; +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + /* SoC card */ static struct snd_soc_card snd_soc_card_cnl = { .name = "cnl-audio", @@ -211,6 +215,7 @@ static struct snd_soc_card snd_soc_card_cnl = { .num_dapm_routes = ARRAY_SIZE(cnl_map), .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), + .add_dai_link = cnl_add_dai_link, .fully_routed = true, }; From d0061f7c863bfe1b2f615fc1cf631933bfe7c29c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 14:02:58 -0500 Subject: [PATCH 213/285] Revert "ASoC: Intel: add rt274 machine driver for cnl" This reverts commit 18ba099b5a2129ecb23069dea662191e955f9dfa. --- sound/soc/intel/boards/Kconfig | 12 -- sound/soc/intel/boards/Makefile | 2 - sound/soc/intel/boards/cnl_rt274.c | 247 ----------------------------- 3 files changed, 261 deletions(-) delete mode 100644 sound/soc/intel/boards/cnl_rt274.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 44168d6f0fd541..97854ecf9cb0ea 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -318,16 +318,4 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH endif ## SND_SOC_INTEL_SKYLAKE -if SND_SOC_INTEL_SKL || SND_SOC_SOF_CANNONLAKE - -config SND_SOC_INTEL_CNL_RT274_MACH - tristate "ASoC Audio driver for CNL with RT274 in I2S Mode" - select SND_SOC_RT274 - help - This adds support for ASoC machine driver for CNL and codec RT274. This - will create an alsa sound card. Say Y if you have such a device If - unsure select "N". - -endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_CANNONLAKE - endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 8c7d03abeff397..bbaf3d7f56a525 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -23,7 +23,6 @@ snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o -snd-soc-cnl-rt274-objs := cnl_rt274.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -49,4 +48,3 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o -obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c deleted file mode 100644 index 6f8be5a4d508aa..00000000000000 --- a/sound/soc/intel/boards/cnl_rt274.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * cnl_rt274.c - ASOC Machine driver for CNL - * - * Copyright (C) 2016 Intel Corp - * Author: Guneshwor Singh - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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; version 2 of the License. - * - * 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. - * - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#include -#include -#include -#include -#include "../../codecs/rt274.h" - -#define CNL_FREQ_OUT 24000000 -#define CNL_BE_FIXUP_RATE 48000 -#define RT274_CODEC_DAI "rt274-aif1" - -static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - struct snd_soc_dapm_context *dapm = w->dapm; - struct snd_soc_card *card = dapm->card; - int ret = 0; - int ratio = 100; - - struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, - RT274_CODEC_DAI); - if (!codec_dai) - return -EINVAL; - - ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL1, - CNL_FREQ_OUT, - SND_SOC_CLOCK_IN); - if (ret) { - dev_err(codec_dai->dev, - "failed to enable PLL1: %d\n", ret); - } - - snd_soc_dai_set_bclk_ratio(codec_dai, ratio); - - return ret; -} - -/* Headset jack detection DAPM pins */ -static struct snd_soc_jack_pin cnl_headset_pins[] = { - { - .pin = "Mic Jack", - .mask = SND_JACK_MICROPHONE, - }, - { - .pin = "Headphone Jack", - .mask = SND_JACK_HEADPHONE, - }, -}; - -static const struct snd_kcontrol_new cnl_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone Jack"), - SOC_DAPM_PIN_SWITCH("Mic Jack"), -}; - -static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("Mic Jack", NULL), - SND_SOC_DAPM_MIC("SoC DMIC", NULL), - SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, - cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), -}; - -static const struct snd_soc_pcm_stream dai_params_codec = { - .formats = SNDRV_PCM_FMTBIT_S24_LE, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, -}; - -static const struct snd_soc_dapm_route cnl_map[] = { - {"Headphone Jack", NULL, "HPO Pin"}, - {"MIC", NULL, "Mic Jack"}, - {"DMic", NULL, "SoC DMIC"}, - {"DMIC01 Rx", NULL, "Capture"}, - {"dmic01_hifi", NULL, "DMIC01 Rx"}, - - {"AIF1 Playback", NULL, "ssp0 Tx"}, - {"ssp0 Tx", NULL, "codec1_out"}, - {"ssp0 Tx", NULL, "codec0_out"}, - - {"ssp0 Rx", NULL, "AIF1 Capture"}, - {"codec0_in", NULL, "ssp0 Rx"}, - - {"Headphone Jack", NULL, "Platform Clock"}, - {"MIC", NULL, "Platform Clock"}, -}; - -static struct snd_soc_jack cnl_headset; - -static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) -{ - int ret; - struct snd_soc_codec *codec = runtime->codec; - struct snd_soc_card *card = runtime->card; - struct snd_soc_dai *codec_dai = runtime->codec_dai; - - ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET, &cnl_headset, - cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); - - if (ret) - return ret; - - snd_soc_codec_set_jack(codec, &cnl_headset, NULL); - - /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); - if (ret < 0) { - dev_err(runtime->dev, "can't set codec pcm format %d\n", ret); - return ret; - } - - card->dapm.idle_bias_off = true; - - return 0; -} - -static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *rate = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - - rate->min = rate->max = CNL_BE_FIXUP_RATE; - channels->min = channels->max = 2; - snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); - snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - SNDRV_PCM_FORMAT_S24_LE); - - return 0; -} - -static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = 2; - - return 0; -} - -static const char pname[] = "0000:00:1f.3"; -static const char cname[] = "i2c-INT34C2:00"; - -struct snd_soc_dai_link cnl_rt274_dailink[] = { - { - .name = "SSP0-Codec", - .cpu_dai_name = "SSP0 Pin", - .codec_name = cname, - .codec_dai_name = "rt274-aif1", - .platform_name = pname, - .be_hw_params_fixup = cnl_be_fixup, - .ignore_pmdown_time = 1, - .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, - .dpcm_playback = 1, - .dpcm_capture = 1, - .init = cnl_rt274_init, - }, - { - .name = "dmic01", - .cpu_dai_name = "DMIC01 Pin", - .codec_name = "dmic-codec", - .codec_dai_name = "dmic-hifi", - .platform_name = pname, - .ignore_suspend = 1, - .no_pcm = 1, - .dpcm_capture = 1, - .be_hw_params_fixup = cnl_dmic_fixup, - }, -}; - -static int -cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) -{ - link->platform_name = pname; - link->nonatomic = 1; - - return 0; -} - -/* SoC card */ -static struct snd_soc_card snd_soc_card_cnl = { - .name = "cnl-audio", - .dai_link = cnl_rt274_dailink, - .num_links = ARRAY_SIZE(cnl_rt274_dailink), - .dapm_widgets = cnl_rt274_widgets, - .num_dapm_widgets = ARRAY_SIZE(cnl_rt274_widgets), - .dapm_routes = cnl_map, - .num_dapm_routes = ARRAY_SIZE(cnl_map), - .controls = cnl_controls, - .num_controls = ARRAY_SIZE(cnl_controls), - .add_dai_link = cnl_add_dai_link, - .fully_routed = true, -}; - -static int cnl_rt274_probe(struct platform_device *pdev) -{ - snd_soc_card_cnl.dev = &pdev->dev; - - return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); -} - -static const struct platform_device_id cnl_board_ids[] = { - { .name = "cnl_rt274" }, - { } -}; - -static struct platform_driver cnl_rt274_driver = { - .driver = { - .name = "cnl_rt274", - .pm = &snd_soc_pm_ops, - }, - .probe = cnl_rt274_probe, - .id_table = cnl_board_ids, -}; - -module_platform_driver(cnl_rt274_driver); - -MODULE_AUTHOR("Guneshwor Singh "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:cnl_rt274"); From c360652e4dbff06a73e3f08da05eb3d12a9c7525 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 23 Nov 2016 19:05:46 +0530 Subject: [PATCH 214/285] ASoC: Intel: Boards: Add CNL RT274 I2S machine driver Add the CNL I2S machine driver using Realtek ALC274 codec in I2S mode. Change-Id: Ife808f52d69e73a8156130c446a3ab0602fff63d Signed-off-by: Guneshwor Singh Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/12406 Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/Kconfig | 16 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt274.c | 385 +++++++++++++++++++++++++++++ 3 files changed, 403 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_rt274.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 97854ecf9cb0ea..4f1dca3b953760 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -318,4 +318,20 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH endif ## SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKYLAKE + +config SND_SOC_INTEL_CNL_RT274_MACH + tristate "Cannonlake with RT274 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT274 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Cannonlake platform + with RT274 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + +endif ## SND_SOC_INTEL_SKYLAKE + endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index bbaf3d7f56a525..8c7d03abeff397 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -23,6 +23,7 @@ snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o +snd-soc-cnl-rt274-objs := cnl_rt274.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -48,3 +49,4 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c new file mode 100644 index 00000000000000..1b788f8e59a22f --- /dev/null +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -0,0 +1,385 @@ +/* + * cnl_rt274.c - ASOC Machine driver for CNL + * + * Copyright (C) 2016 Intel Corp + * Author: Guneshwor Singh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../codecs/rt274.h" + +static struct snd_soc_jack cnl_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin cnl_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new cnl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static const struct snd_soc_dapm_route cnl_map[] = { + {"Headphone Jack", NULL, "HPO Pin"}, + {"MIC", NULL, "Mic Jack"}, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + + /* ssp2 path */ + {"Dummy Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "ssp2_out"}, + + {"ssp2 Rx", NULL, "Dummy Capture"}, + {"ssp2_in", NULL, "ssp2 Rx"}, + + /* ssp1 path */ + {"Dummy Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "ssp1_out"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "codec1_out"}, + {"ssp0 Tx", NULL, "codec0_out"}, + + {"ssp0 Rx", NULL, "AIF1 Capture"}, + {"codec0_in", NULL, "ssp0 Rx"}, +}; + +static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *codec_dai = runtime->codec_dai; + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + + if (ret) + return ret; + + snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(runtime->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + return 0; +} + +static unsigned int rates_supported[] = { + 48000, + 32000, + 24000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list rate_constraints = { + .count = ARRAY_SIZE(rates_supported), + .list = rates_supported, +}; + +static int cnl_fe_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &rate_constraints); +} + +static struct snd_soc_ops cnl_fe_ops = { + .startup = cnl_fe_startup, +}; + +static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +#define CNL_FREQ_OUT 19200000 + +static int rt274_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret, ratio = 100; + + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + ratio * params_rate(params), CNL_FREQ_OUT); + if (ret != 0) { + dev_err(rtd->dev, "Failed to enable PLL2 with Ref Clock Loop: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, CNL_FREQ_OUT, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops rt274_ops = { + .hw_params = rt274_hw_params, +}; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static const char pname[] = "0000:02:18.0"; +static const char cname[] = "rt274.0-001c"; +#else +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "i2c-INT34C2:00"; +#endif + +static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { + { + .name = "CNL Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_fe_ops, + }, + { + .name = "CNL Deepbuffer Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dpcm_playback = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &cnl_fe_ops, + }, + { + .name = "CNL Reference Port", + .stream_name = "Reference Capture", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + /* Trace Buffer DAI links */ + { + .name = "CNL Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer2", + .stream_name = "Core 2 Trace Buffer", + .cpu_dai_name = "TraceBuffer2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer3", + .stream_name = "Core 3 Trace Buffer", + .cpu_dai_name = "TraceBuffer3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + /* Probe DAI-links */ + { + .name = "CNL Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + { + .name = "CNL Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + /* back ends */ + { + .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = cnl_rt274_init, + .ops = &rt274_ops, + }, + { + .name = "SSP1-Codec", + .id = 2, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + }, + { + .name = "dmic01", + .id = 3, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = pname, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl = { + .name = "cnl-audio", + .dai_link = cnl_rt274_msic_dailink, + .num_links = ARRAY_SIZE(cnl_rt274_msic_dailink), + .dapm_widgets = cnl_rt274_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_rt274_widgets), + .dapm_routes = cnl_map, + .num_dapm_routes = ARRAY_SIZE(cnl_map), + .controls = cnl_controls, + .num_controls = ARRAY_SIZE(cnl_controls), +}; + +static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) +{ + snd_soc_card_cnl.dev = &pdev->dev; + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); +} + +static struct platform_driver snd_cnl_rt274_driver = { + .driver = { + .name = "cnl_rt274", + .pm = &snd_soc_pm_ops, + }, + .probe = snd_cnl_rt274_mc_probe, +}; + +module_platform_driver(snd_cnl_rt274_driver); + +MODULE_AUTHOR("Guneshwor Singh "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_rt274"); From 527fe4652cdc094a6dc85d802d9d9002e97014bd Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 27 Feb 2017 09:09:37 +0530 Subject: [PATCH 215/285] ASoC: Intel: board: Add support for dynamic FE dai link in cnl_rt274 machine FE dai links now come from topology, so remove them from machine driver. Additionally register ops to initialize dai link. Rate constraint is not required as rates will come from topology. So remove the startup ops as well which sets the rate constraint. Change-Id: I0fb07c74450bf55415323539e383ef39ed3ff4c4 Signed-off-by: Guneshwor Singh Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/13924 Reviewed-by: Koul, Vinod Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Reviewed-by: Nc, Shreyas Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 73 ++++-------------------------- 1 file changed, 10 insertions(+), 63 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 1b788f8e59a22f..436a11d9a4f8c1 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -124,30 +124,6 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) return 0; } -static unsigned int rates_supported[] = { - 48000, - 32000, - 24000, - 16000, - 8000, -}; - -static struct snd_pcm_hw_constraint_list rate_constraints = { - .count = ARRAY_SIZE(rates_supported), - .list = rates_supported, -}; - -static int cnl_fe_startup(struct snd_pcm_substream *substream) -{ - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &rate_constraints); -} - -static struct snd_soc_ops cnl_fe_ops = { - .startup = cnl_fe_startup, -}; - static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -204,45 +180,6 @@ static const char cname[] = "i2c-INT34C2:00"; #endif static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { - { - .name = "CNL Audio Port", - .stream_name = "Audio", - .cpu_dai_name = "System Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .ops = &cnl_fe_ops, - }, - { - .name = "CNL Deepbuffer Port", - .stream_name = "Deep Buffer Audio", - .cpu_dai_name = "Deepbuffer Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_playback = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .ops = &cnl_fe_ops, - }, - { - .name = "CNL Reference Port", - .stream_name = "Reference Capture", - .cpu_dai_name = "Reference Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, /* Trace Buffer DAI links */ { .name = "CNL Trace Buffer0", @@ -351,6 +288,15 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { }, }; +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + /* SoC card */ static struct snd_soc_card snd_soc_card_cnl = { .name = "cnl-audio", @@ -362,6 +308,7 @@ static struct snd_soc_card snd_soc_card_cnl = { .num_dapm_routes = ARRAY_SIZE(cnl_map), .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), + .add_dai_link = cnl_add_dai_link, }; static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) From 8d9beeb9e01cae110329070679bce455edb6735e Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 4 Apr 2017 08:34:51 +0530 Subject: [PATCH 216/285] ASoC: Intel: board: Move cnl_rt274 clock setting to supply widget During BE-BE loop, codec clocks were not set as it was a part of dai link ops hw_params and no sound was heard due to this reason when use cases involve BE-BE loop. So, move codec clock setting as a part of supply widget and define routes appropriately. Also use macro to define BE rate fixup and use it for both fixup as well as clock computation. Change-Id: Id5a08d2bd6024a61b601dbbe70ad99a52149da5e Signed-off-by: Guneshwor Singh Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/14595 Reviewed-by: Prusty, Subhransu S Reviewed-by: Kale, Sanyog R Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt274.c | 89 +++++++++++++++++++----------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 436a11d9a4f8c1..b67330dcfa07d8 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -35,6 +35,56 @@ #include "../../codecs/rt274.h" +#define CNL_FREQ_OUT 19200000 +#define CNL_BE_FIXUP_RATE 48000 +#define RT274_CODEC_DAI "rt274-aif1" + +static struct snd_soc_dai *cnl_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + + return NULL; +} + +static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + int ret = 0, ratio = 100; + struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, + RT274_CODEC_DAI); + + /* Codec needs clock for Jack detection and button press */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, + CNL_FREQ_OUT, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "set codec sysclk failed: %d\n", ret); + return ret; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + CNL_BE_FIXUP_RATE * ratio, + CNL_FREQ_OUT); + if (ret) { + dev_err(codec_dai->dev, + "failed to enable PLL2: %d\n", ret); + return ret; + } + } + + return ret; +} + static struct snd_soc_jack cnl_headset; /* Headset jack detection DAPM pins */ @@ -58,6 +108,9 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, @@ -94,6 +147,9 @@ static const struct snd_soc_dapm_route cnl_map[] = { {"ssp0 Rx", NULL, "AIF1 Capture"}, {"codec0_in", NULL, "ssp0 Rx"}, + + {"Headphone Jack", NULL, "Platform Clock"}, + {"MIC", NULL, "Platform Clock"}, }; static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) @@ -132,7 +188,7 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = 48000; + rate->min = rate->max = CNL_BE_FIXUP_RATE; channels->min = channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), @@ -141,36 +197,6 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -#define CNL_FREQ_OUT 19200000 - -static int rt274_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret, ratio = 100; - - snd_soc_dai_set_bclk_ratio(codec_dai, ratio); - - ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, - ratio * params_rate(params), CNL_FREQ_OUT); - if (ret != 0) { - dev_err(rtd->dev, "Failed to enable PLL2 with Ref Clock Loop: %d\n", ret); - return ret; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, CNL_FREQ_OUT, - SND_SOC_CLOCK_IN); - if (ret < 0) - dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); - - return ret; -} - -static struct snd_soc_ops rt274_ops = { - .hw_params = rt274_hw_params, -}; - #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) static const char pname[] = "0000:02:18.0"; static const char cname[] = "rt274.0-001c"; @@ -260,7 +286,6 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_playback = 1, .dpcm_capture = 1, .init = cnl_rt274_init, - .ops = &rt274_ops, }, { .name = "SSP1-Codec", From 298a4ef346d196c445ed3e28b2f6a5fd17ee3f48 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 15 Jun 2017 15:08:06 +0530 Subject: [PATCH 217/285] ASoC: Intel: boards: Remove SSP1-codec dai link from cnl_rt274 machine Since NHLT does not have SSP1 endpoint, remove it from the dai link definitions Change-Id: I7b08f43d21eeff9decb5722e3af4f142f800b3f7 Signed-off-by: Guneshwor Singh Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/15999 Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: Babu, Ramesh Reviewed-by: audio_build Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index b67330dcfa07d8..677724273c2630 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -287,21 +287,9 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .init = cnl_rt274_init, }, - { - .name = "SSP1-Codec", - .id = 2, - .cpu_dai_name = "SSP1 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .be_hw_params_fixup = cnl_be_fixup, - .ignore_suspend = 1, - .no_pcm = 1, - .dpcm_playback = 1, - }, { .name = "dmic01", - .id = 3, + .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", From 98f0d4d09310549528ffbd670c23cb4c719a8f12 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:55 +0530 Subject: [PATCH 218/285] ASoC: Intel: Skylake: Fix codec_dai NULL pointer dereferening Pointer 'codec_dai' returned from call to cnl_get_codec_dai() can be NULL. Check for the valid pointer before dereferencing. Change-Id: I783b6220e32a9b8bf7655b92df7a4b034175a509 Signed-off-by: Pankaj Bharadiya Reviewed-on: https://git-gar-1.devtools.intel.com/gerrit/18681 Reviewed-by: Prusty, Subhransu S Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 677724273c2630..6a048213cf509c 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -60,6 +60,8 @@ static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, int ret = 0, ratio = 100; struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, RT274_CODEC_DAI); + if (!codec_dai) + return -EINVAL; /* Codec needs clock for Jack detection and button press */ ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, From 6e550a2c659e9e6b99eb62649a56394bde053729 Mon Sep 17 00:00:00 2001 From: Shahina Shaik Date: Mon, 30 Apr 2018 18:47:02 +0530 Subject: [PATCH 219/285] ASoC: Intel: Boards: Replace codec to component in RT274 machine driver As the framework is changed in kernel 4.17 version, replace codec variable with component and use component specific function to set jack. Change-Id: Id6d1cda7968a5d524a3210f1b38221214c2bb67d Signed-off-by: Shahina Shaik --- sound/soc/intel/boards/cnl_rt274.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 6a048213cf509c..0926e6c4b6ebc1 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -157,7 +157,7 @@ static const struct snd_soc_dapm_route cnl_map[] = { static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_component *component = runtime->codec_dai->component;; struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; @@ -168,7 +168,7 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + snd_soc_component_set_jack(component, &cnl_headset, NULL); /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); From ac25e50743f5753c38ba67684b3c699f6cb3f585 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 23 Jun 2018 09:50:50 -0500 Subject: [PATCH 220/285] ASoC: Intel: cnl-rt274: fix compilation warning Needs to be squashed with component model update, kept separate to avoid merge issues with SST code Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cnl_rt274.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 0926e6c4b6ebc1..2d55e4d878b70e 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -157,7 +157,7 @@ static const struct snd_soc_dapm_route cnl_map[] = { static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_component *component = runtime->codec_dai->component;; + struct snd_soc_component *component = runtime->codec_dai->component; struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; From 776b10beb418c8713dc0cc8668951bd439f8a7d9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 23 Jun 2018 09:27:25 -0500 Subject: [PATCH 221/285] ASoC: Intel: cnl-rt274: fix clock settings Use 24MHz instead of 19.2 (Mandatory rework) and use helper to find the dai Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cnl_rt274.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 2d55e4d878b70e..0dacf9401e087e 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -35,30 +35,19 @@ #include "../../codecs/rt274.h" -#define CNL_FREQ_OUT 19200000 +#define CNL_FREQ_OUT 24000000 #define CNL_BE_FIXUP_RATE 48000 #define RT274_CODEC_DAI "rt274-aif1" -static struct snd_soc_dai *cnl_get_codec_dai(struct snd_soc_card *card, - const char *dai_name) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strcmp(rtd->codec_dai->name, dai_name)) - return rtd->codec_dai; - } - - return NULL; -} - static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) + struct snd_kcontrol *k, int event) { struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; - int ret = 0, ratio = 100; - struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, + int ret = 0; + int ratio = 100; + + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT274_CODEC_DAI); if (!codec_dai) return -EINVAL; From 31e20067dacbeb5191c0570f8f82b4c8b02055f8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 May 2018 20:43:51 -0500 Subject: [PATCH 222/285] ASoC: Intel: make cnl_rt274 work with SOF (1)refine machine driver to make it work with sof (2)disable DMIC for it is not ready now Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 2 +- sound/soc/intel/boards/cnl_rt274.c | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 4f1dca3b953760..771bdf483d3814 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -318,7 +318,7 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH endif ## SND_SOC_INTEL_SKYLAKE -if SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_CANNONLAKE config SND_SOC_INTEL_CNL_RT274_MACH tristate "Cannonlake with RT274 I2S mode" diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 0dacf9401e087e..98a7b3015c0d23 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -100,10 +100,11 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, - cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -113,6 +114,7 @@ static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +#endif static const struct snd_soc_dapm_route cnl_map[] = { {"Headphone Jack", NULL, "HPO Pin"}, @@ -151,8 +153,9 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dai *codec_dai = runtime->codec_dai; ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET, &cnl_headset, - cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, + ARRAY_SIZE(cnl_headset_pins)); if (ret) return ret; @@ -172,15 +175,17 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) } static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params) { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = CNL_BE_FIXUP_RATE; - channels->min = channels->max = 2; + rate->min = CNL_BE_FIXUP_RATE; + rate->max = CNL_BE_FIXUP_RATE; + channels->min = 2; + channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); @@ -197,6 +202,7 @@ static const char cname[] = "i2c-INT34C2:00"; #endif static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) /* Trace Buffer DAI links */ { .name = "CNL Trace Buffer0", @@ -261,6 +267,7 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .ignore_suspend = 1, .nonatomic = 1, }, +#endif /* back ends */ { .name = "SSP0-Codec", @@ -268,7 +275,6 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .cpu_dai_name = "SSP0 Pin", .codec_name = cname, .codec_dai_name = "rt274-aif1", - .platform_name = pname, .be_hw_params_fixup = cnl_be_fixup, .ignore_suspend = 1, .no_pcm = 1, @@ -278,18 +284,19 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .init = cnl_rt274_init, }, +#if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) { .name = "dmic01", .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", - .platform_name = pname, .ignore_suspend = 1, .no_pcm = 1, .dpcm_capture = 1, .be_hw_params_fixup = cnl_dmic_fixup, }, +#endif }; static int @@ -313,6 +320,7 @@ static struct snd_soc_card snd_soc_card_cnl = { .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), .add_dai_link = cnl_add_dai_link, + .fully_routed = true, }; static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) From 6d34036a190145ad72122cc8b627d791b8c3f87e Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 29 Jun 2018 14:24:51 +0800 Subject: [PATCH 223/285] Asoc: Intel: refine rt274 machine driver for SOF Add SOF platform name Signed-off-by: Rander Wang --- sound/soc/intel/boards/cnl_rt274.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 98a7b3015c0d23..0689c63304ea46 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -197,7 +197,11 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, static const char pname[] = "0000:02:18.0"; static const char cname[] = "rt274.0-001c"; #else +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) +static const char pname[] = "sof-audio"; +#else static const char pname[] = "0000:00:1f.3"; +#endif static const char cname[] = "i2c-INT34C2:00"; #endif From a94477214fc1943aea748fc45aeaf700401a51c8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 18:33:39 -0500 Subject: [PATCH 224/285] [SQUASHME] ASoC: SOF: intel: bdw: make variables static Detected with Sparse, variables need to be static if not declared Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/bdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 47d3896329d25b..dc21d1fac43323 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -730,7 +730,7 @@ static struct snd_soc_dai_driver bdw_dai[] = { }, }; -struct snd_sof_dai_drv bdw_dai_drv = { +static struct snd_sof_dai_drv bdw_dai_drv = { .drv = bdw_dai, .num_drv = ARRAY_SIZE(bdw_dai) }; From c50e4aca64a475732849d0c6d6c76330ceca6dc7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 18:35:45 -0500 Subject: [PATCH 225/285] [SQUASHME] ASoC: SOF: intel: byt: make variables static Detected with Sparse, variables need to be static if not declared Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/byt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 77a2c7a041cc96..49ec019b45d0fa 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -799,12 +799,12 @@ static struct snd_soc_dai_driver byt_dai[] = { }, }; -struct snd_sof_dai_drv byt_dai_drv = { +static struct snd_sof_dai_drv byt_dai_drv = { .drv = byt_dai, .num_drv = 3, /* we have only 3 SSPs on byt*/ }; -struct snd_sof_dai_drv cht_dai_drv = { +static struct snd_sof_dai_drv cht_dai_drv = { .drv = byt_dai, /* all 6 SSPs may be available for cherrytrail */ .num_drv = ARRAY_SIZE(byt_dai), From 5def138b098ff854b7cf17180d2dbd83d262d39c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 18:36:37 -0500 Subject: [PATCH 226/285] [SQUASHME] ASoC: SOF: intel: hda-dai: add include file Detected with Sparse, variables need to be static if not declared Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dai.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 091fcc1a7e66a0..9df9c4990eb109 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -10,6 +10,7 @@ #include #include "../sof-priv.h" +#include "hda.h" #define SKL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) From 96845d5f39734bd0f91bf65a11d7f50aafc08189 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 18:37:57 -0500 Subject: [PATCH 227/285] [SQUASHME] ASoC: SOF: intel: hsw: make variables static Detected with Sparse, variables need to be static if not declared Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hsw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c index 5e2c50b47b7502..1a8cbd3af50e13 100644 --- a/sound/soc/sof/intel/hsw.c +++ b/sound/soc/sof/intel/hsw.c @@ -730,7 +730,7 @@ static struct snd_soc_dai_driver hsw_dai[] = { }, }; -struct snd_sof_dai_drv hsw_dai_drv = { +static struct snd_sof_dai_drv hsw_dai_drv = { .drv = hsw_dai, .num_drv = ARRAY_SIZE(hsw_dai) }; From 631f2a07f25a36dbc4ade8648a3f0081287c447f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 2 Jul 2018 18:38:47 -0500 Subject: [PATCH 228/285] [SQUASHME] ASoC: SOF: intel: skl: remove unused function Detected with Sparse, remove for now Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/skl.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index adb85a579b697e..86097bbea1ad58 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -40,11 +40,6 @@ static const struct snd_sof_debugfs_map skl_dsp_debugfs[] = { {"dsp", HDA_DSP_BAR, 0, 0x10000}, }; -int skl_run_firmware(struct snd_sof_dev *sdev) -{ - return 0; -} - /* skylake ops */ struct snd_sof_dsp_ops sof_skl_ops = { /* probe and remove */ From 04ac398b62e6e06b7565f7bbd8889582f7efd24c Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 3 Jul 2018 10:41:41 +0800 Subject: [PATCH 229/285] [SQUASHME] ASoC: SOF: intel: fix a typo in hda-dai Should be 8 channel for SSP2 Rx, correct it. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-dai.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 9df9c4990eb109..59e41ee6907210 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -39,7 +39,7 @@ static struct snd_soc_dai_driver skl_dai[] = { .name = "SSP2 Pin", .playback = SOF_DAI_STREAM("ssp2 Tx", 1, 8, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), - .capture = SOF_DAI_STREAM("ssp2 Rx", 1, 16, + .capture = SOF_DAI_STREAM("ssp2 Rx", 1, 8, SNDRV_PCM_RATE_8000_192000, SKL_FORMATS), }, { From f9ab80f566cfcff9e03eaa5e5bbdc510767cd818 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 3 Jul 2018 12:33:03 -0500 Subject: [PATCH 230/285] [SQUASHME] ASoC: Intel: fix Kconfig typos Fix ASoC: Intel: select relevant machine drivers for SOF s/INTEL_SKL/INTEL_SKYLAKE Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 771bdf483d3814..0b3ee787bad7ef 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -220,9 +220,9 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". -endif ## SND_SOC_INTEL_SKL +endif ## SND_SOC_INTEL_SKYLAKE -if SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE +if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_APOLLOLAKE config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A in I2S Mode" @@ -270,9 +270,9 @@ config SND_SOC_INTEL_BXT_TDF8532_MACH Say Y if you have such a device. If unsure select "N". -endif ## SND_SOC_INTEL_SKL || SND_SOC_SOF_APOLLOLAKE +endif ## SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_APOLLOLAKE -if SND_SOC_INTEL_SKL +if SND_SOC_INTEL_SKYLAKE config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH tristate "KBL with RT5663 and MAX98927 in I2S Mode" From 0fee76c9ff8cfcdb89bf5967cf14b7dd44a0e15f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 3 Jul 2018 20:39:22 -0500 Subject: [PATCH 231/285] [SQUASHME] ASoC: SOF: add mm.h 0-day complains that on some architectures such as m68k mm_struct is not defined. Make sure it's added. In file included from ./arch/m68k/include/asm/pgtable_mm.h:148, from ./arch/m68k/include/asm/pgtable.h:5, from sound/soc/sof/pm.c:15: ./arch/m68k/include/asm/motorola_pgtable.h: In function 'pgd_offset': ./arch/m68k/include/asm/motorola_pgtable.h:199:11: error: dereferencing pointer to incomplete type 'const struct mm_struct' return mm->pgd + pgd_index(address); ^~ In file included from ./arch/m68k/include/asm/pgtable_mm.h:148, from ./arch/m68k/include/asm/pgtable.h:5, from sound/soc/sof/core.c:15: ./arch/m68k/include/asm/motorola_pgtable.h: In function 'pgd_offset': ./arch/m68k/include/asm/motorola_pgtable.h:199:11: error: dereferencing pointer to incomplete type 'const struct mm_struct' return mm->pgd + pgd_index(address); Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/core.c | 1 + sound/soc/sof/pm.c | 1 + 2 files changed, 2 insertions(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index ce315938243a73..de87e8c6766184 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -8,6 +8,7 @@ * Author: Liam Girdwood */ +#include #include #include #include diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index d825ff402fc66a..56c60423e85c84 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -8,6 +8,7 @@ * Author: Liam Girdwood */ +#include #include #include #include From 28fc73b252eaa6438e9c10a854f7067db24f07cd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 3 Jul 2018 21:00:13 -0500 Subject: [PATCH 232/285] [SQUASHME] ASoC: SOF: ops: fix uninitialized use warning For some architectures/compilers, 0-day reports errors sound/soc/sof/ops.c: In function 'snd_sof_pci_update_bits_unlocked': sound/soc/sof/ops.c:26:6: warning: 'ret' is used uninitialized in this function [-Wuninitialized] u32 ret; ^~~ Fix by forcing value to ~0 (as done in pci_read_config_dword) Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index a3b1aebb0fd14a..0668f64e1ef672 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -23,7 +23,7 @@ int snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, { bool change; unsigned int old, new; - u32 ret; + u32 ret = ~0; /* explicit init to remove uninitialized use warnings */ pci_read_config_dword(sdev->pci, offset, &ret); dev_dbg(sdev->dev, "Debug PCIR: %8.8x at %8.8x\n", From ad83ac7e64cf4d396c3f3c59618160ed8f8cce5d Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 2 Jul 2018 15:30:20 +0100 Subject: [PATCH 233/285] [SQUASHME] ASoC: sof: core: cleanup - add comments and reorder funcs Add comments describing funcs/code blocks and reorder funcs so features are grouped. Signed-off-by: Liam Girdwood --- sound/soc/sof/core.c | 87 +++++++++++++++++++++++++--------------- sound/soc/sof/sof-priv.h | 19 +++++---- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index b523496612d5ae..7d633449ac0e5f 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -19,8 +19,13 @@ #include "sof-priv.h" #include "ops.h" -#define TIMEOUT_IPC 5 -#define TIMEOUT_BOOT 100 +/* SOF defaults if not provided by the platform in ms */ +#define TIMEOUT_DEFAULT_IPC 5 +#define TIMEOUT_DEFAULT_BOOT 100 + +/* + * Generic object lookup APIs. + */ struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd) @@ -122,11 +127,16 @@ static inline unsigned int sof_get_pages(size_t size) return (size + PAGE_SIZE - 1) >> PAGE_SHIFT; } +/* + * FW Panic/fault handling. + */ + struct sof_panic_msg { u32 id; const char *msg; }; +/* standard FW panic types */ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_MEM, "out of memory"}, {SOF_IPC_PANIC_WORK, "work subsystem init failed"}, @@ -177,6 +187,43 @@ int snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, } EXPORT_SYMBOL(snd_sof_get_status); +/* + * Generic buffer page table creation. + */ + +int snd_sof_create_page_table(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size) +{ + int i, pages; + + pages = snd_sgbuf_aligned_pages(size); + + dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n", + dmab->area, size, pages); + + for (i = 0; i < pages; i++) { + u32 idx = (((i << 2) + i)) >> 1; + u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; + u32 *pg_table; + + dev_dbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + + pg_table = (u32 *)(page_table + idx); + + if (i & 1) + *pg_table |= (pfn << 4); + else + *pg_table |= pfn; + } + + return pages; +} + +/* + * SOF Driver enumeration. + */ + static int sof_probe(struct platform_device *pdev) { struct snd_sof_pdata *plat_data = dev_get_platdata(&pdev->dev); @@ -210,17 +257,17 @@ static int sof_probe(struct platform_device *pdev) spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); - /* set up platform and component drivers */ + /* set up platform component driver */ snd_sof_new_platform_drv(sdev); snd_sof_new_dai_drv(sdev); /* set default timeouts if none provided */ if (plat_data->desc->ipc_timeout == 0) - sdev->ipc_timeout = TIMEOUT_IPC; + sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC; else sdev->ipc_timeout = plat_data->desc->ipc_timeout; if (plat_data->desc->boot_timeout == 0) - sdev->boot_timeout = TIMEOUT_BOOT; + sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT; else sdev->boot_timeout = plat_data->desc->boot_timeout; @@ -279,8 +326,10 @@ static int sof_probe(struct platform_device *pdev) goto comp_err; } + /* init DMA trace */ ret = snd_sof_init_trace(sdev); if (ret < 0) { + /* non fatal */ dev_warn(sdev->dev, "warning: failed to initialize trace %d\n", ret); } @@ -321,34 +370,6 @@ void snd_sof_shutdown(struct device *dev) } EXPORT_SYMBOL(snd_sof_shutdown); -int snd_sof_create_page_table(struct snd_sof_dev *sdev, - struct snd_dma_buffer *dmab, - unsigned char *page_table, size_t size) -{ - int i, pages; - - pages = sof_get_pages(size); - - dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n", - dmab->area, size, pages); - - for (i = 0; i < pages; i++) { - u32 idx = (((i << 2) + i)) >> 1; - u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; - u32 *pg_table; - - dev_dbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); - - pg_table = (u32 *)(page_table + idx); - - if (i & 1) - *pg_table |= (pfn << 4); - else - *pg_table |= pfn; - } - - return pages; -} static struct platform_driver sof_driver = { .driver = { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index fabfcb25180940..89f71951988169 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -112,21 +112,20 @@ struct snd_sof_dsp_ops { void (*dbg_dump)(struct snd_sof_dev *sof_dev, u32 flags); /* connect pcm substream to a host stream */ - int (*host_stream_open)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); + int (*pcm_open)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); /* disconnect pcm substream to a host stream */ - int (*host_stream_close)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); + int (*pcm_close)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); /* host stream hw params */ - int (*host_stream_hw_params)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params); + int (*pcm_hw_params)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); /* host stream trigger */ - int (*host_stream_trigger)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - int cmd); + int (*pcm_trigger)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd); /* FW loading */ int (*load_firmware)(struct snd_sof_dev *sof_dev, From 73eb318245f81d76bbf9fed30aec00a8c78c2ce5 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 2 Jul 2018 15:30:38 +0100 Subject: [PATCH 234/285] [SQUASHME] ASoC: sof: pcm: cleanup - add comments and reorder funcs Add comments describing funcs/code blocks and reorder funcs so features are grouped. Signed-off-by: Liam Girdwood --- sound/soc/sof/pcm.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 558d054af826cd..76199046bb9bb8 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -6,6 +6,8 @@ * Copyright(c) 2017 Intel Corporation. All rights reserved. * * Author: Liam Girdwood + * + * PCM Layer, interface between ALSA and IPC. */ #include @@ -25,6 +27,7 @@ #include #include #include "sof-priv.h" +#include "ops.h" /* Create DMA buffer page table for DSP */ static int create_page_table(struct snd_pcm_substream *substream, @@ -49,7 +52,6 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_dev *sdev = snd_soc_platform_get_drvdata(rtd->platform); - const struct snd_sof_dsp_ops *ops = sdev->ops; struct snd_sof_pcm *spcm = rtd->sof; struct sof_ipc_pcm_params pcm; struct sof_ipc_pcm_params_reply ipc_params_reply; @@ -134,11 +136,11 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream, } /* firmware already configured host stream */ - if (ops && ops->host_stream_hw_params) { - pcm.params.stream_tag = - ops->host_stream_hw_params(sdev, substream, params); - dev_dbg(sdev->dev, "stream_tag %d", pcm.params.stream_tag); - } + pcm.params.stream_tag = snd_sof_pcm_platform_hw_params(sdev, + substream, + params); + dev_dbg(sdev->dev, "stream_tag %d", pcm.params.stream_tag); + /* send IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), @@ -197,7 +199,6 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_sof_pcm *spcm = rtd->sof; struct sof_ipc_stream stream; struct sof_ipc_reply reply; - const struct snd_sof_dsp_ops *ops = sdev->ops; int ret = 0; /* nothing todo for BE */ @@ -233,8 +234,7 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } /* set RUN firstly per the sequence suggested by firmware team */ - if (ops && ops->host_stream_trigger) - ret = ops->host_stream_trigger(sdev, substream, cmd); + snd_sof_pcm_platform_trigger(sdev, substream, cmd); /* send IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, @@ -255,7 +255,7 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream) if (rtd->dai_link->no_pcm) return 0; - /* TODO: call HW position callback */ + /* read position from DSP */ host = bytes_to_frames(substream->runtime, spcm->stream[substream->stream].posn.host_posn); dai = bytes_to_frames(substream->runtime, @@ -276,7 +276,6 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) struct snd_sof_pcm *spcm = rtd->sof; struct snd_soc_tplg_stream_caps *caps = &spcm->pcm.caps[substream->stream]; - const struct snd_sof_dsp_ops *ops = sdev->ops; /* nothing todo for BE */ if (rtd->dai_link->no_pcm) @@ -320,18 +319,14 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) dev_dbg(sdev->dev, "buffer max %zd bytes\n", runtime->hw.buffer_bytes_max); - // TODO: create IPC to get this from DSP pipeline - //runtime->hw.fifo_size = hw->fifo_size; - /* set wait time - TODO: come from topology */ - snd_pcm_wait_time(substream, 500); + substream->wait_time = 500; spcm->stream[substream->stream].posn.host_posn = 0; spcm->stream[substream->stream].posn.dai_posn = 0; spcm->stream[substream->stream].substream = substream; - if (ops && ops->host_stream_open) - ops->host_stream_open(sdev, substream); + snd_sof_pcm_platform_open(sdev, substream); mutex_unlock(&spcm->mutex); return 0; @@ -343,7 +338,6 @@ static int sof_pcm_close(struct snd_pcm_substream *substream) struct snd_sof_dev *sdev = snd_soc_platform_get_drvdata(rtd->platform); struct snd_sof_pcm *spcm = rtd->sof; - const struct snd_sof_dsp_ops *ops = sdev->ops; /* nothing todo for BE */ if (rtd->dai_link->no_pcm) @@ -352,8 +346,7 @@ static int sof_pcm_close(struct snd_pcm_substream *substream) dev_dbg(sdev->dev, "pcm: close stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); - if (ops && ops->host_stream_close) - ops->host_stream_close(sdev, substream); + snd_sof_pcm_platform_close(sdev, substream); mutex_lock(&spcm->mutex); pm_runtime_mark_last_busy(sdev->dev); @@ -382,8 +375,8 @@ static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd) struct snd_soc_tplg_stream_caps *caps; int ret = 0, stream = SNDRV_PCM_STREAM_PLAYBACK; + /* find SOF PCM for this RTD */ spcm = snd_sof_find_spcm_dai(sdev, rtd); - if (!spcm) { dev_warn(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); @@ -486,6 +479,7 @@ static void sof_pcm_free(struct snd_pcm *pcm) snd_sof_free_topology(sdev); } +/* fixup the BE DAI link to match any values from topology */ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -499,8 +493,10 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_sof_dai *dai = snd_sof_find_dai(sdev, (char *)rtd->dai_link->name); + /* no topology exists for this BE, try a common configuration */ if (!dai) { - dev_err(sdev->dev, "No DAI is found!\n"); + dev_err(sdev->dev, "error: no topology found for BE DAI %s config\n", + rtd->dai_link->name); /* set 48k, stereo, 16bits by default */ rate->min = 48000; @@ -512,7 +508,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, snd_mask_none(fmt); snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); - return -EINVAL; + return 0; } /* read format from topology */ From 81063c85e9ed7f63175547607572dfc6094e5eaa Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 2 Jul 2018 15:30:49 +0100 Subject: [PATCH 235/285] [SQUASHME] ASoC: sof: loader: cleanup - add comments and reorder funcs Add comments describing funcs/code blocks and reorder funcs so features are grouped. Signed-off-by: Liam Girdwood --- sound/soc/sof/loader.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 82d6c6ccc65fb2..4bc7d7ac8ff59c 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -6,6 +6,8 @@ * Copyright(c) 2017 Intel Corporation. All rights reserved. * * Author: Liam Girdwood + * + * Generic firmware loader. */ #include @@ -42,6 +44,7 @@ static int get_ext_windows(struct snd_sof_dev *sdev, return ret; } +/* parse the extended FW boot data structures from FW boot message */ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) { struct sof_ipc_ext_data_hdr *ext_hdr; @@ -279,5 +282,6 @@ EXPORT_SYMBOL(snd_sof_run_firmware); void snd_sof_fw_unload(struct snd_sof_dev *sdev) { + /* TODO: support module unloading at runtime */ } EXPORT_SYMBOL(snd_sof_fw_unload); From dc9bfad004772168c81a8cdb1b178c813d6a8119 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 2 Jul 2018 15:30:59 +0100 Subject: [PATCH 236/285] [SQUASHME] ASoC: sof: ipc: cleanup - add comments and reorder funcs Add comments describing funcs/code blocks and reorder funcs so features are grouped. Signed-off-by: Liam Girdwood --- sound/soc/sof/ipc.c | 364 +++++++++++++++++++++++++------------------- 1 file changed, 206 insertions(+), 158 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index bd646c23cf9fd2..f0c90a7f820b2b 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -6,6 +6,9 @@ * Copyright(c) 2017 Intel Corporation. All rights reserved. * * Author: Liam Girdwood + * + * Generic IPC layer that can work over MMIO and SPI/I2C. PHY layer provided + * by platform driver code. */ #include @@ -31,11 +34,20 @@ #include "sof-priv.h" #include "ops.h" -/* IPC message timeout (msecs) */ +/* + * IPC message default size and timeout (msecs). + * TODO: allow platforms to set size and timeout. + */ #define IPC_TIMEOUT_MSECS 300 - #define IPC_EMPTY_LIST_SIZE 8 +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id); +static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd); + +/* + * IPC message Tx/Rx message handling. + */ + /* SOF generic IPC data */ struct snd_sof_ipc { struct snd_sof_dev *sdev; @@ -59,6 +71,7 @@ static struct snd_sof_ipc_msg *msg_get_empty(struct snd_sof_ipc *ipc) { struct snd_sof_ipc_msg *msg = NULL; + /* get first empty message in the list */ if (!list_empty(&ipc->empty_list)) { msg = list_first_entry(&ipc->empty_list, struct snd_sof_ipc_msg, list); @@ -68,6 +81,7 @@ static struct snd_sof_ipc_msg *msg_get_empty(struct snd_sof_ipc *ipc) return msg; } +/* wait for IPC message reply */ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, void *reply_data) { @@ -111,6 +125,7 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, return ret; } +/* send IPC message from host to DSP */ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes) @@ -121,6 +136,7 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, spin_lock_irqsave(&sdev->ipc_lock, flags); + /* get an empty message */ msg = msg_get_empty(ipc); if (!msg) { spin_unlock_irqrestore(&sdev->ipc_lock, flags); @@ -132,9 +148,11 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, msg->reply_size = reply_bytes; msg->complete = false; + /* attach any data */ if (msg_bytes) memcpy(msg->msg_data, msg_data, msg_bytes); + /* add message to transmit list */ list_add_tail(&msg->list, &ipc->tx_list); /* schedule the messgae if not busy */ @@ -143,10 +161,12 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, spin_unlock_irqrestore(&sdev->ipc_lock, flags); + /* now wait for completion */ return tx_wait_done(ipc, msg, reply_data); } EXPORT_SYMBOL(sof_ipc_tx_message); +/* send next IPC message in list */ static void ipc_tx_next_msg(struct work_struct *work) { struct snd_sof_ipc *ipc = @@ -156,19 +176,22 @@ static void ipc_tx_next_msg(struct work_struct *work) spin_lock_irq(&sdev->ipc_lock); + /* send message if HW read and message in TX list */ if (list_empty(&ipc->tx_list)) goto out; + /* sned first message in TX list */ msg = list_first_entry(&ipc->tx_list, struct snd_sof_ipc_msg, list); list_move(&msg->list, &ipc->reply_list); - snd_sof_dsp_send_msg(sdev, msg); + dev_dbg(sdev->dev, "ipc: send 0x%x\n", msg->header); out: spin_unlock_irq(&sdev->ipc_lock); } +/* find original TX message from DSP reply */ struct snd_sof_ipc_msg *sof_ipc_reply_find_msg(struct snd_sof_ipc *ipc, u32 header) { @@ -192,7 +215,7 @@ struct snd_sof_ipc_msg *sof_ipc_reply_find_msg(struct snd_sof_ipc *ipc, } EXPORT_SYMBOL(sof_ipc_reply_find_msg); -/* locks held by caller */ +/* mark IPC message as complete - locks held by caller */ void sof_ipc_tx_msg_reply_complete(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg) { @@ -200,6 +223,7 @@ void sof_ipc_tx_msg_reply_complete(struct snd_sof_ipc *ipc, wake_up(&msg->waitq); } +/* drop all IPC messages in preparation for DSP stall/reset */ void sof_ipc_drop_all(struct snd_sof_ipc *ipc) { struct snd_sof_dev *sdev = ipc->sdev; @@ -223,6 +247,7 @@ void sof_ipc_drop_all(struct snd_sof_ipc *ipc) } EXPORT_SYMBOL(sof_ipc_drop_all); +/* handle reply message from DSP */ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) { struct snd_sof_ipc_msg *msg; @@ -239,17 +264,105 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) } EXPORT_SYMBOL(snd_sof_ipc_reply); -int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, - size_t dspbox_size, u32 hostbox, - size_t hostbox_size) +/* DSP firmware has sent host a message */ +static void ipc_msgs_rx(struct work_struct *work) { - sdev->dsp_box.offset = dspbox; - sdev->dsp_box.size = dspbox_size; - sdev->host_box.offset = hostbox; - sdev->host_box.size = hostbox_size; - return 0; + struct snd_sof_ipc *ipc = + container_of(work, struct snd_sof_ipc, rx_kwork); + struct snd_sof_dev *sdev = ipc->sdev; + struct sof_ipc_hdr hdr; + u32 cmd, type; + int err = -EINVAL; + + /* read back header */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &hdr, sizeof(hdr)); + + cmd = hdr.cmd & SOF_GLB_TYPE_MASK; + type = hdr.cmd & SOF_CMD_TYPE_MASK; + + /* check message type */ + switch (cmd) { + case SOF_IPC_GLB_REPLY: + dev_err(sdev->dev, "error: ipc reply unknown\n"); + break; + case SOF_IPC_FW_READY: + /* check for FW boot completion */ + if (!sdev->boot_complete) { + if (sdev->ops->fw_ready) + err = sdev->ops->fw_ready(sdev, cmd); + if (err < 0) { + dev_err(sdev->dev, "DSP firmware boot timeout %d\n", + err); + } else { + /* firmware boot completed OK */ + sdev->boot_complete = true; + dev_dbg(sdev->dev, "booting DSP firmware completed\n"); + wake_up(&sdev->boot_wait); + } + } + break; + case SOF_IPC_GLB_COMPOUND: + case SOF_IPC_GLB_TPLG_MSG: + case SOF_IPC_GLB_PM_MSG: + case SOF_IPC_GLB_COMP_MSG: + break; + case SOF_IPC_GLB_STREAM_MSG: + /* need to pass msg id into the function */ + ipc_stream_message(sdev, hdr.cmd); + break; + case SOF_IPC_GLB_TRACE_MSG: + ipc_trace_message(sdev, type); + break; + default: + dev_err(sdev->dev, "unknown DSP message 0x%x\n", cmd); + break; + } + + dev_dbg(sdev->dev, "ipc rx: 0x%x done\n", hdr.cmd); + + /* tell DSP we are done */ + snd_sof_dsp_cmd_done(sdev); } -EXPORT_SYMBOL(snd_sof_dsp_mailbox_init); + +/* schedule work to transmit any IPC in queue */ +void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->tx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_tx); + +/* schedule work to handle IPC from DSP */ +void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) +{ + schedule_work(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); + +/* + * IPC trace mechanism. + */ + +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct sof_ipc_dma_trace_posn posn; + + switch (msg_id) { + case SOF_IPC_TRACE_DMA_POSITION: + /* read back full message */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, + sizeof(posn)); + snd_sof_trace_update_pos(sdev, &posn); + break; + default: + dev_err(sdev->dev, "error: unhandled trace message %x\n", + msg_id); + break; + } +} + +/* + * IPC stream position. + */ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) { @@ -293,6 +406,7 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) snd_pcm_period_elapsed(spcm->stream[direction].substream); } +/* DSP notifies host of an XRUN within FW */ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) { struct sof_ipc_stream_posn posn; @@ -300,7 +414,7 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) u32 posn_offset; int direction; - /* check if we have stream box */ + /* check if we have stream MMIO on this platform */ if (sdev->stream_box.size == 0) { /* read back full message */ snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, @@ -330,12 +444,14 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", posn.host_posn, posn.xrun_comp_id, posn.xrun_size); - return; /* TODO: don't do anything yet until preload is working */ - +#if defined(CONFIG_SOC_SOF_DEBUG_XRUN_STOP) + /* stop PCM on XRUN - used for pipeline debug */ memcpy(&spcm->stream[direction].posn, &posn, sizeof(posn)); snd_pcm_stop_xrun(spcm->stream[direction].substream); +#endif } +/* stream notifications from DSP FW */ static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) { /* get msg cmd type and msd id */ @@ -356,148 +472,7 @@ static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) } } -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) -{ - struct sof_ipc_dma_trace_posn posn; - - switch (msg_id) { - case SOF_IPC_TRACE_DMA_POSITION: - /* read back full message */ - snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &posn, - sizeof(posn)); - snd_sof_trace_update_pos(sdev, &posn); - break; - default: - dev_err(sdev->dev, "error: unhandled trace message %x\n", - msg_id); - break; - } -} - -/* DSP firmware has sent host a message */ -static void ipc_msgs_rx(struct work_struct *work) -{ - struct snd_sof_ipc *ipc = - container_of(work, struct snd_sof_ipc, rx_kwork); - struct snd_sof_dev *sdev = ipc->sdev; - struct sof_ipc_hdr hdr; - u32 cmd, type; - int err = -EINVAL; - - /* read back header */ - snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, &hdr, sizeof(hdr)); - - cmd = hdr.cmd & SOF_GLB_TYPE_MASK; - type = hdr.cmd & SOF_CMD_TYPE_MASK; - - switch (cmd) { - case SOF_IPC_GLB_REPLY: - dev_err(sdev->dev, "error: ipc reply unknown\n"); - break; - case SOF_IPC_FW_READY: - /* check for FW boot completion */ - if (!sdev->boot_complete) { - if (sdev->ops->fw_ready) - err = sdev->ops->fw_ready(sdev, cmd); - if (err < 0) { - dev_err(sdev->dev, "DSP firmware boot timeout %d\n", - err); - } else { - /* firmware boot completed OK */ - sdev->boot_complete = true; - dev_dbg(sdev->dev, "booting DSP firmware completed\n"); - wake_up(&sdev->boot_wait); - } - } - break; - case SOF_IPC_GLB_COMPOUND: - case SOF_IPC_GLB_TPLG_MSG: - case SOF_IPC_GLB_PM_MSG: - case SOF_IPC_GLB_COMP_MSG: - break; - case SOF_IPC_GLB_STREAM_MSG: - /* need to pass msg id into the function */ - ipc_stream_message(sdev, hdr.cmd); - break; - case SOF_IPC_GLB_TRACE_MSG: - ipc_trace_message(sdev, type); - break; - default: - dev_err(sdev->dev, "unknown DSP message 0x%x\n", cmd); - break; - } - - dev_dbg(sdev->dev, "ipc rx: 0x%x done\n", hdr.cmd); - - snd_sof_dsp_cmd_done(sdev); -} - -void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev) -{ - schedule_work(&sdev->ipc->tx_kwork); -} -EXPORT_SYMBOL(snd_sof_ipc_msgs_tx); - -void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) -{ - schedule_work(&sdev->ipc->rx_kwork); -} -EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); - -struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc *ipc; - struct snd_sof_ipc_msg *msg; - int i; - - ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL); - if (!ipc) - return NULL; - - INIT_LIST_HEAD(&ipc->tx_list); - INIT_LIST_HEAD(&ipc->reply_list); - INIT_LIST_HEAD(&ipc->empty_list); - init_waitqueue_head(&ipc->wait_txq); - INIT_WORK(&ipc->tx_kwork, ipc_tx_next_msg); - INIT_WORK(&ipc->rx_kwork, ipc_msgs_rx); - ipc->sdev = sdev; - - /* pre-allocate messages */ - dev_dbg(sdev->dev, "pre-allocate %d IPC messages\n", - IPC_EMPTY_LIST_SIZE); - msg = devm_kzalloc(sdev->dev, sizeof(struct snd_sof_ipc_msg) * - IPC_EMPTY_LIST_SIZE, GFP_KERNEL); - if (!msg) - return NULL; - - /* pre-allocate message data */ - for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { - msg->msg_data = devm_kzalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); - if (!msg->msg_data) - return NULL; - - msg->reply_data = devm_kzalloc(sdev->dev, PAGE_SIZE, - GFP_KERNEL); - if (!msg->reply_data) - return NULL; - - init_waitqueue_head(&msg->waitq); - list_add(&msg->list, &ipc->empty_list); - msg++; - } - - return ipc; -} -EXPORT_SYMBOL(snd_sof_ipc_init); - -void snd_sof_ipc_free(struct snd_sof_dev *sdev) -{ - /* TODO: send IPC to prepare DSP for shutdown */ - cancel_work_sync(&sdev->ipc->tx_kwork); - cancel_work_sync(&sdev->ipc->rx_kwork); -} -EXPORT_SYMBOL(snd_sof_ipc_free); - +/* get stream position IPC - use faster MMIO method if available on platform */ int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int direction, struct sof_ipc_stream_posn *posn) @@ -524,6 +499,10 @@ int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev, } EXPORT_SYMBOL(snd_sof_ipc_stream_posn); +/* + * IPC get()/set() for kcontrols. + */ + int snd_sof_ipc_set_comp_data(struct snd_sof_ipc *ipc, struct snd_sof_control *scontrol, u32 ipc_cmd, enum sof_ipc_ctrl_type ctrl_type, @@ -607,3 +586,72 @@ int snd_sof_ipc_get_comp_data(struct snd_sof_ipc *ipc, return 0; } EXPORT_SYMBOL(snd_sof_ipc_get_comp_data); + +/* + * IPC layer enumeration. + */ + +int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, + size_t dspbox_size, u32 hostbox, + size_t hostbox_size) +{ + sdev->dsp_box.offset = dspbox; + sdev->dsp_box.size = dspbox_size; + sdev->host_box.offset = hostbox; + sdev->host_box.size = hostbox_size; + return 0; +} +EXPORT_SYMBOL(snd_sof_dsp_mailbox_init); + +struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc *ipc; + struct snd_sof_ipc_msg *msg; + int i; + + ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL); + if (!ipc) + return NULL; + + INIT_LIST_HEAD(&ipc->tx_list); + INIT_LIST_HEAD(&ipc->reply_list); + INIT_LIST_HEAD(&ipc->empty_list); + init_waitqueue_head(&ipc->wait_txq); + INIT_WORK(&ipc->tx_kwork, ipc_tx_next_msg); + INIT_WORK(&ipc->rx_kwork, ipc_msgs_rx); + ipc->sdev = sdev; + + /* pre-allocate messages */ + dev_dbg(sdev->dev, "pre-allocate %d IPC messages\n", + IPC_EMPTY_LIST_SIZE); + msg = devm_kzalloc(sdev->dev, sizeof(struct snd_sof_ipc_msg) * + IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (!msg) + return NULL; + + /* pre-allocate message data */ + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + msg->msg_data = devm_kzalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); + if (!msg->msg_data) + return NULL; + + msg->reply_data = devm_kzalloc(sdev->dev, PAGE_SIZE, + GFP_KERNEL); + if (!msg->reply_data) + return NULL; + + init_waitqueue_head(&msg->waitq); + list_add(&msg->list, &ipc->empty_list); + msg++; + } + + return ipc; +} +EXPORT_SYMBOL(snd_sof_ipc_init); + +void snd_sof_ipc_free(struct snd_sof_dev *sdev) +{ + cancel_work_sync(&sdev->ipc->tx_kwork); + cancel_work_sync(&sdev->ipc->rx_kwork); +} +EXPORT_SYMBOL(snd_sof_ipc_free); From 621a20389a496720ce1193eff6f00b172a5481e0 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 2 Jul 2018 15:31:15 +0100 Subject: [PATCH 237/285] [SQUASHME] ASoC: sof: ops: cleanup - add comments and reorder funcs Add comments describing funcs/code blocks and reorder funcs so features are grouped. Signed-off-by: Liam Girdwood --- sound/soc/sof/ops.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 1a00a4b7d8037f..340f1ccc40447f 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -219,6 +219,51 @@ static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd) return 0; } +/* host PCM ops */ +static inline int +snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + if (sdev->ops && sdev->ops->pcm_open) + return sdev->ops->pcm_open(sdev, substream); + else + return 0; +} + +/* disconnect pcm substream to a host stream */ +static inline int +snd_sof_pcm_platform_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + if (sdev->ops && sdev->ops->pcm_close) + return sdev->ops->pcm_close(sdev, substream); + else + return 0; +} + +/* host stream hw params */ +static inline int +snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + if (sdev->ops && sdev->ops->pcm_hw_params) + return sdev->ops->pcm_hw_params(sdev, substream, params); + else + return 0; +} + +/* host stream trigger */ +static inline int +snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, int cmd) +{ + if (sdev->ops && sdev->ops->pcm_trigger) + return sdev->ops->pcm_trigger(sdev, substream, cmd); + else + return 0; +} + int snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value); From cb84cdb5a892b05bc2db12fc57740a319a535115 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 2 Jul 2018 15:31:29 +0100 Subject: [PATCH 238/285] [SQUASHME] ASoC: sof: skl, cnl, apl: cleanup - add comments and reorder funcs Add comments describing funcs/code blocks and reorder funcs so features are grouped. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/apl.c | 8 ++++---- sound/soc/sof/intel/cnl.c | 8 ++++---- sound/soc/sof/intel/skl.c | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index d36e30d2a8b388..358c6904f1115e 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -77,10 +77,10 @@ struct snd_sof_dsp_ops sof_apl_ops = { .dbg_dump = hda_dsp_dump, /* stream callbacks */ - .host_stream_open = hda_dsp_pcm_open, - .host_stream_close = hda_dsp_pcm_close, - .host_stream_hw_params = hda_dsp_pcm_hw_params, - .host_stream_trigger = hda_dsp_pcm_trigger, + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 80d6cc772fd74a..5fad5fcd02327c 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -205,10 +205,10 @@ struct snd_sof_dsp_ops sof_cnl_ops = { .dbg_dump = hda_dsp_dump, /* stream callbacks */ - .host_stream_open = hda_dsp_pcm_open, - .host_stream_close = hda_dsp_pcm_close, - .host_stream_hw_params = hda_dsp_pcm_hw_params, - .host_stream_trigger = hda_dsp_pcm_trigger, + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index d4b6de626456b1..2e62a85bafc6cc 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -82,10 +82,10 @@ struct snd_sof_dsp_ops sof_skl_ops = { .dbg_dump = hda_dsp_dump, /* stream callbacks */ - .host_stream_open = hda_dsp_pcm_open, - .host_stream_close = hda_dsp_pcm_close, - .host_stream_hw_params = hda_dsp_pcm_hw_params, - .host_stream_trigger = hda_dsp_pcm_trigger, + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_trigger = hda_dsp_pcm_trigger, /* firmware loading */ .load_firmware = hda_dsp_cl_load_fw, From b66e8ef1122d04210a1829c9245b0e1d7b37b5bc Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 16:39:48 +0100 Subject: [PATCH 239/285] [SQUASH] ASoC: SOF: Kconfig: cleanup Kconfig debug options Signed-off-by: Liam Girdwood --- sound/soc/sof/Kconfig | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index bdae3f5969915b..cec2ea91df14dc 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -7,7 +7,6 @@ config SND_SOC_SOF_ACPI config SND_SOC_SOF_PLATFORM tristate - config SND_SOC_SOF tristate "Sound Open Firmware Support" default m @@ -29,9 +28,19 @@ config SND_SOC_SOF_NOCODEC Say Y if you need this nocodec fallback option If unsure select "N". +config SND_SOC_SOF_DEBUG + bool "SOF debugging features" + depends on SND_SOC_SOF + help + This option can be used to enable or disable individual SOF firmware + and driver debugging options. + Say Y if you are debugging SOF FW or drivers. + If unsure select "N". + config SND_SOC_SOF_FORCE_NOCODEC_MODE - tristate "SOF force nocodec Mode" + bool "SOF force nocodec Mode" depends on SND_SOC_SOF_NOCODEC + depends on SND_SOC_SOF_DEBUG help This forces SOF to use dummy/nocodec as machine driver, even though there is a codec detected on the real platform. This is @@ -41,5 +50,14 @@ config SND_SOC_SOF_FORCE_NOCODEC_MODE Say Y if you need this force nocodec mode option If unsure select "N". +config SND_SOC_SOF_DEBUG_XRUN_STOP + bool "SOF stop on XRUN" + depends on SND_SOC_SOF_DEBUG + help + This option forces PCMs to stop on any XRUN event. This is useful to + preserve any trace data ond pipeline status prior to the XRUN. + Say Y if you are debugging SOF FW pipeline XRUNs. + If unsure select "N". + source "sound/soc/sof/intel/Kconfig" source "sound/soc/sof/xtensa/Kconfig" From 94001aa81cf3f921a689e4b557fb487c1943dd3c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:34:26 +0100 Subject: [PATCH 240/285] [SQUASHME] ASoC: SOF: core fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/core.c | 4 ++-- sound/soc/sof/sof-priv.h | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index d022deca0d7786..ee76082cd891a9 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -34,7 +34,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm = NULL; list_for_each_entry(spcm, &sdev->pcm_list, list) { - if (spcm->pcm.dai_id == rtd->dai_link->id) + if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id) return spcm; } @@ -87,7 +87,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm = NULL; list_for_each_entry(spcm, &sdev->pcm_list, list) { - if (spcm->pcm.pcm_id == pcm_id) + if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id) return spcm; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index d0c26dcb932a2b..91115c82e62506 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -107,9 +107,9 @@ struct snd_sof_dsp_ops { /* mailbox */ void (*mailbox_read)(struct snd_sof_dev *sof_dev, u32 offset, - void __iomem *addr, size_t bytes); + void *addr, size_t bytes); void (*mailbox_write)(struct snd_sof_dev *sof_dev, u32 offset, - void __iomem *addr, size_t bytes); + void *addr, size_t bytes); /* ipc */ int (*send_msg)(struct snd_sof_dev *sof_dev, @@ -171,7 +171,14 @@ struct sof_ops_table { }; /* FS entry for debug files that can expose DSP memories, registers */ -struct snd_sof_dfsentry { +struct snd_sof_dfsentry_io { + struct dentry *dfsentry; + size_t size; + void __iomem *buf; + struct snd_sof_dev *sdev; +}; + +struct snd_sof_dfsentry_buf { struct dentry *dfsentry; size_t size; void *buf; @@ -401,6 +408,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, int *direction); struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, unsigned int pcm_id); +void sof_ipc_drop_all(struct snd_sof_ipc *ipc); /* * Stream IPC From d6c73564cffbc3ad7885e933e6fcc5734142e099 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:34:45 +0100 Subject: [PATCH 241/285] [SQUASHME] ASoC: SOF: debug: fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/debug.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index c1580a17d2f2d2..ec966d25816df4 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -32,7 +32,7 @@ static int sof_dfsentry_open(struct inode *inode, struct file *file) static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dfsentry_io *dfse = file->private_data; struct snd_sof_dev *sdev = dfse->sdev; int size; u32 *buf; @@ -78,7 +78,7 @@ int snd_sof_debugfs_create_item(struct snd_sof_dev *sdev, void __iomem *base, size_t size, const char *name) { - struct snd_sof_dfsentry *dfse; + struct snd_sof_dfsentry_io *dfse; if (!sdev) return -EINVAL; @@ -118,7 +118,8 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) for (i = 0; i < ops->debug_map_count; i++) { map = &ops->debug_map[i]; - err = snd_sof_debugfs_create_item(sdev, sdev->bar[map->bar] + + err = snd_sof_debugfs_create_item(sdev, + sdev->bar[map->bar] + map->offset, map->size, map->name); if (err < 0) From 798b7aabf7a170d608c812e7b548ed46e3295b68 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:35:37 +0100 Subject: [PATCH 242/285] [SQUASHME] ASoC: SOF: ipc: fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/ipc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index f0c90a7f820b2b..300f33139862a9 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -192,8 +192,8 @@ static void ipc_tx_next_msg(struct work_struct *work) } /* find original TX message from DSP reply */ -struct snd_sof_ipc_msg *sof_ipc_reply_find_msg(struct snd_sof_ipc *ipc, - u32 header) +static struct snd_sof_ipc_msg *sof_ipc_reply_find_msg(struct snd_sof_ipc *ipc, + u32 header) { struct snd_sof_dev *sdev = ipc->sdev; struct snd_sof_ipc_msg *msg; @@ -213,11 +213,10 @@ struct snd_sof_ipc_msg *sof_ipc_reply_find_msg(struct snd_sof_ipc *ipc, header); return NULL; } -EXPORT_SYMBOL(sof_ipc_reply_find_msg); /* mark IPC message as complete - locks held by caller */ -void sof_ipc_tx_msg_reply_complete(struct snd_sof_ipc *ipc, - struct snd_sof_ipc_msg *msg) +static void sof_ipc_tx_msg_reply_complete(struct snd_sof_ipc *ipc, + struct snd_sof_ipc_msg *msg) { msg->complete = true; wake_up(&msg->waitq); From 3fbc96aaceceffd226200b3c4ba85ca89a6e7e06 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:35:50 +0100 Subject: [PATCH 243/285] [SQUASHME] ASoC: SOF: topology: fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/topology.c | 120 +++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 61 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f9347384ecf3ad..143a191b381ad0 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -216,7 +216,7 @@ static const struct sof_frame_types sof_frames[] = { {"float", SOF_IPC_FRAME_FLOAT}, }; -static enum sof_ipc_dai_type find_format(const char *name) +static enum sof_ipc_frame find_format(const char *name) { int i; @@ -244,28 +244,25 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct sof_ipc_ctrl_data *cdata; /* validate topology data */ - if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) + if (le32_to_cpu(mc->num_channels) >= SND_SOC_TPLG_MAX_CHAN) return -EINVAL; /* init the volume get/put data */ scontrol->size = sizeof(struct sof_ipc_ctrl_data) + - sizeof(struct sof_ipc_ctrl_value_chan) * mc->num_channels; + sizeof(struct sof_ipc_ctrl_value_chan) * + le32_to_cpu(mc->num_channels); scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); cdata = scontrol->control_data; if (!scontrol->control_data) return -ENOMEM; scontrol->comp_id = sdev->next_comp_id; - scontrol->num_channels = mc->num_channels; + scontrol->num_channels = le32_to_cpu(mc->num_channels); dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", scontrol->comp_id, scontrol->num_channels); return 0; - /* configure channel IDs */ - //for (i = 0; i < mc->num_channels; i++) { - // v.pcm.chmap[i] = mc->channel[i].id; - //} } struct sof_topology_token { @@ -281,7 +278,7 @@ static int get_token_u32(void *elem, void *object, u32 offset, u32 size) struct snd_soc_tplg_vendor_value_elem *velem = elem; u32 *val = object + offset; - *val = velem->value; + *val = le32_to_cpu(velem->value); return 0; } @@ -495,7 +492,7 @@ static void sof_parse_uuid_tokens(struct snd_soc_component *scomp, int i, j; /* parse element by element */ - for (i = 0; i < array->num_elems; i++) { + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { elem = &array->uuid[i]; /* search for token */ @@ -505,7 +502,7 @@ static void sof_parse_uuid_tokens(struct snd_soc_component *scomp, continue; /* match token id */ - if (tokens[j].token != elem->token) + if (tokens[j].token != le32_to_cpu(elem->token)) continue; /* matched - now load token */ @@ -525,7 +522,7 @@ static void sof_parse_string_tokens(struct snd_soc_component *scomp, int i, j; /* parse element by element */ - for (i = 0; i < array->num_elems; i++) { + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { elem = &array->string[i]; /* search for token */ @@ -535,7 +532,7 @@ static void sof_parse_string_tokens(struct snd_soc_component *scomp, continue; /* match token id */ - if (tokens[j].token != elem->token) + if (tokens[j].token != le32_to_cpu(elem->token)) continue; /* matched - now load token */ @@ -559,7 +556,7 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, u32 *index = NULL; /* parse element by element */ - for (i = 0; i < array->num_elems; i++) { + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { elem = &array->value[i]; /* search for token */ @@ -570,7 +567,7 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, continue; /* match token id */ - if (tokens[j].token != elem->token) + if (tokens[j].token != le32_to_cpu(elem->token)) continue; /* pdm config array index */ @@ -583,7 +580,8 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp, /* inc number of pdm array index */ if (index) - ++(*index); + (*index)++; + /* fallthrough */ case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable: case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable: case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A: @@ -625,7 +623,7 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, int asize; while (priv_size > 0) { - asize = array->size; + asize = le32_to_cpu(array->size); /* validate asize */ if (asize < 0) { /* FIXME: A zero-size array makes no sense */ @@ -643,7 +641,7 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, } /* call correct parser depending on type */ - switch (array->type) { + switch (le32_to_cpu(array->type)) { case SND_SOC_TPLG_TUPLE_TYPE_UUID: sof_parse_uuid_tokens(scomp, object, tokens, count, array); @@ -702,7 +700,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, scontrol->sdev = sdev; mutex_init(&scontrol->mutex); - switch (hdr->ops.info) { + switch (le32_to_cpu(hdr->ops.info)) { case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: @@ -825,16 +823,16 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &comp_dai, dai_tokens, ARRAY_SIZE(dai_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse dai tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } ret = sof_parse_tokens(scomp, &comp_dai.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse dai.cfg tokens failed %d\n", private->size); @@ -880,7 +878,7 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &buffer, buffer_tokens, ARRAY_SIZE(buffer_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse buffer tokens failed %d\n", private->size); @@ -921,7 +919,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &host, pcm_tokens, ARRAY_SIZE(pcm_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse host tokens failed %d\n", private->size); @@ -930,10 +928,10 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &host.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse host.cfg tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -982,7 +980,7 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, ret = sof_parse_tokens(scomp, &pipeline, sched_tokens, ARRAY_SIZE(sched_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse pipeline tokens failed %d\n", private->size); @@ -1022,7 +1020,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &mixer.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse mixer.cfg tokens failed %d\n", private->size); @@ -1054,7 +1052,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, const unsigned int *p; int ret, tlv[TLV_ITEMS]; - if (tw->num_kcontrols != 1) { + if (le32_to_cpu(tw->num_kcontrols) != 1) { dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n", tw->num_kcontrols); return -EINVAL; @@ -1092,7 +1090,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &volume, volume_tokens, ARRAY_SIZE(volume_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse volume tokens failed %d\n", private->size); @@ -1100,10 +1098,10 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, } ret = sof_parse_tokens(scomp, &volume.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse volume.cfg tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1138,7 +1136,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &src, src_tokens, ARRAY_SIZE(src_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse src tokens failed %d\n", private->size); @@ -1147,10 +1145,10 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &src.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse src.cfg tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1187,19 +1185,19 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &tone, tone_tokens, ARRAY_SIZE(tone_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse tone tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } ret = sof_parse_tokens(scomp, &tone.config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse tone.cfg tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1473,21 +1471,21 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, ret = sof_parse_tokens(scomp, &config->ssp, ssp_tokens, ARRAY_SIZE(ssp_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse ssp tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } - config->ssp.mclk_rate = hw_config->mclk_rate; - config->ssp.bclk_rate = hw_config->bclk_rate; - config->ssp.fsync_rate = hw_config->fsync_rate; - config->ssp.tdm_slots = hw_config->tdm_slots; - config->ssp.tdm_slot_width = hw_config->tdm_slot_width; + config->ssp.mclk_rate = le32_to_cpu(hw_config->mclk_rate); + config->ssp.bclk_rate = le32_to_cpu(hw_config->bclk_rate); + config->ssp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->ssp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + config->ssp.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); config->ssp.mclk_direction = hw_config->mclk_direction; - config->ssp.rx_slots = hw_config->rx_slots; - config->ssp.tx_slots = hw_config->tx_slots; + config->ssp.rx_slots = le32_to_cpu(hw_config->rx_slots); + config->ssp.tx_slots = le32_to_cpu(hw_config->tx_slots); dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d\n", config->id, config->format, @@ -1525,10 +1523,10 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, /* get DMIC tokens */ ret = sof_parse_tokens(scomp, &config->dmic, dmic_tokens, ARRAY_SIZE(dmic_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse dmic tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1557,10 +1555,10 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, /* get DMIC PDM tokens */ ret = sof_parse_tokens(scomp, &ipc_config->dmic.pdm[0], dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse dmic pdm tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); kfree(ipc_config); return ret; } @@ -1633,10 +1631,10 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index, /* get any bespoke DAI tokens */ ret = sof_parse_tokens(scomp, config, hda_tokens, ARRAY_SIZE(hda_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse hda tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1675,14 +1673,14 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, return 0; /* only support 1 config atm */ - if (cfg->num_hw_configs != 1) { + if (le32_to_cpu(cfg->num_hw_configs) != 1) { dev_err(sdev->dev, "error: unexpected DAI config count %d\n", - cfg->num_hw_configs); + le32_to_cpu(cfg->num_hw_configs)); return -EINVAL; } /* check we have some tokens - we need at least DAI type */ - if (private->size == 0) { + if (le32_to_cpu(private->size) == 0) { dev_err(sdev->dev, "error: expected tokens for DAI, none found\n"); return -EINVAL; } @@ -1692,10 +1690,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, /* get any common DAI tokens */ ret = sof_parse_tokens(scomp, &config, dai_link_tokens, ARRAY_SIZE(dai_link_tokens), private->array, - private->size); + le32_to_cpu(private->size)); if (ret != 0) { dev_err(sdev->dev, "error: parse link tokens failed %d\n", - private->size); + le32_to_cpu(private->size)); return ret; } @@ -1703,8 +1701,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, hw_config = &cfg->hw_config[0]; config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; - config.id = hw_config->id; - config.format = hw_config->fmt; + config.id = le32_to_cpu(hw_config->id); + config.format = le32_to_cpu(hw_config->fmt); /* now load DAI specific data and send IPC - type comes from token */ switch (config.type) { From c023b55fdc08efc8e4e7f88d2265edff44ea4a0f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:36:08 +0100 Subject: [PATCH 244/285] [SQUASHME] ASoC: SOF: trace: fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index dc452ad6109359..d3aa6e4b46a5f7 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -74,7 +74,7 @@ static int sof_wait_trace_avail(struct snd_sof_dev *sdev, size_t *count, static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dfsentry_buf *dfse = file->private_data; struct snd_sof_dev *sdev = dfse->sdev; int err; loff_t pos = *ppos; @@ -130,7 +130,7 @@ static const struct file_operations sof_dfs_trace_fops = { static int trace_debugfs_create(struct snd_sof_dev *sdev) { - struct snd_sof_dfsentry *dfse; + struct snd_sof_dfsentry_buf *dfse; if (!sdev) return -EINVAL; From c764884be50142fd585965070e930f7a7181f26e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:36:20 +0100 Subject: [PATCH 245/285] [SQUASHME] ASoC: SOF: pcm: fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/pcm.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 2ab73d1518c571..92da3074f286ef 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -291,10 +291,10 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) /* set any runtime constraints based on topology */ snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - caps->period_size_min); + le32_to_cpu(caps->period_size_min)); snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - caps->period_size_min); + le32_to_cpu(caps->period_size_min)); /* set runtime config */ runtime->hw.info = SNDRV_PCM_INFO_MMAP | @@ -303,12 +303,12 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; - runtime->hw.formats = caps->formats; - runtime->hw.period_bytes_min = caps->period_size_min; - runtime->hw.period_bytes_max = caps->period_size_max; - runtime->hw.periods_min = caps->periods_min; - runtime->hw.periods_max = caps->periods_max; - runtime->hw.buffer_bytes_max = caps->buffer_size_max; + runtime->hw.formats = le64_to_cpu(caps->formats); + runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min); + runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max); + runtime->hw.periods_min = le32_to_cpu(caps->periods_min); + runtime->hw.periods_max = le32_to_cpu(caps->periods_max); + runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max); dev_dbg(sdev->dev, "period min %zd max %zd bytes\n", runtime->hw.period_bytes_min, @@ -398,8 +398,8 @@ static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd) ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, SNDRV_DMA_TYPE_DEV_SG, sdev->parent, - caps->buffer_size_min, - caps->buffer_size_max); + le32_to_cpu(caps->buffer_size_min), + le32_to_cpu(caps->buffer_size_max)); if (ret) { dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", caps->buffer_size_min, caps->buffer_size_max, @@ -431,8 +431,8 @@ static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd) ret = snd_pcm_lib_preallocate_pages(pcm->streams[stream].substream, SNDRV_DMA_TYPE_DEV_SG, sdev->parent, - caps->buffer_size_min, - caps->buffer_size_max); + le32_to_cpu(caps->buffer_size_min), + le32_to_cpu(caps->buffer_size_max)); if (ret) { dev_err(sdev->dev, "error: can't alloc DMA buffer size 0x%x/0x%x for %s %d\n", caps->buffer_size_min, caps->buffer_size_max, @@ -506,7 +506,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, channels->max = 2; snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S16_LE); return 0; } @@ -516,13 +516,13 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, switch (dai->comp_dai.config.frame_fmt) { case SOF_IPC_FRAME_S16_LE: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S16_LE); break; case SOF_IPC_FRAME_S24_4LE: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S24_LE); break; case SOF_IPC_FRAME_S32_LE: - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + snd_mask_set(fmt, (__force int)SNDRV_PCM_FORMAT_S32_LE); break; default: dev_err(sdev->dev, "No available DAI format!\n"); From e45f078d37849e23b28046a1d7925dbf4be2b260 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:36:39 +0100 Subject: [PATCH 246/285] [SQUASHME] ASoC: SOF: hsw: fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hsw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c index 1a8cbd3af50e13..d81f8de3d65608 100644 --- a/sound/soc/sof/intel/hsw.c +++ b/sound/soc/sof/intel/hsw.c @@ -88,12 +88,12 @@ static void hsw_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, n = size % 4; /* __iowrite32_copy use 32bit size values so divide by 4 */ - __iowrite32_copy((void *)dest, src, m); + __iowrite32_copy(dest, src, m); if (n) { for (i = 0; i < n; i++) tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); - __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + __iowrite32_copy(dest + m * 4, &tmp, 1); } } From cc55919cb2fb71f6da7c26ae5f5cdba8f3f92d3e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:37:22 +0100 Subject: [PATCH 247/285] [SQUASHME] ASoC: SOF: bdw: fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/bdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index dc21d1fac43323..f76463abe07fa5 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -87,12 +87,12 @@ static void bdw_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, n = size % 4; /* __iowrite32_copy use 32bit size values so divide by 4 */ - __iowrite32_copy((void *)dest, src, m); + __iowrite32_copy(dest, src, m); if (n) { for (i = 0; i < n; i++) tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); - __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + __iowrite32_copy(dest + m * 4, &tmp, 1); } } From 0290ba7cb3a7ad62fb2952a8ecfa1727dcf53f76 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:37:38 +0100 Subject: [PATCH 248/285] [SQUASHME] ASoC: SOF: byt: fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/byt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 49ec019b45d0fa..9d06dd312f56d8 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -140,12 +140,12 @@ static void byt_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, n = size % 4; /* __iowrite32_copy use 32bit size values so divide by 4 */ - __iowrite32_copy((void *)dest, src, m); + __iowrite32_copy(dest, src, m); if (n) { for (i = 0; i < n; i++) tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); - __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + __iowrite32_copy(dest + m * 4, &tmp, 1); } } From 7de8df0d32687fb0a9765c44711643a1de4e0fd7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 5 Jul 2018 14:38:07 +0100 Subject: [PATCH 249/285] [SQUASHME] ASoC: SOF: hda: fix sparse warnings. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/hda.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 7949d6d5508025..781a2f4a135ae5 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -77,12 +77,12 @@ void hda_dsp_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, n = size % 4; /* __iowrite32_copy use 32bit size values so divide by 4 */ - __iowrite32_copy((void *)dest, src, m); + __iowrite32_copy(dest, src, m); if (n) { for (i = 0; i < n; i++) tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); - __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + __iowrite32_copy(dest + m * 4, &tmp, 1); } } From 9535ab2b67e1957f70a8fa2482dcb158dfef485b Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 6 Jul 2018 17:35:59 +0800 Subject: [PATCH 250/285] ASoC: SOF: return status in snd_sof_ipc_reply The IPC algorithm depends on the status of snd_sof_ipc_reply. The failed case is processed different with success one Signed-off-by: Rander Wang --- sound/soc/sof/ipc.c | 5 +++-- sound/soc/sof/sof-priv.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 300f33139862a9..6b2b1d12646f6f 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -247,7 +247,7 @@ void sof_ipc_drop_all(struct snd_sof_ipc *ipc) EXPORT_SYMBOL(sof_ipc_drop_all); /* handle reply message from DSP */ -void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) +int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) { struct snd_sof_ipc_msg *msg; @@ -255,11 +255,12 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) if (!msg) { dev_err(sdev->dev, "error: can't find message header 0x%x", msg_id); - return; + return -EINVAL; } /* wake up and return the error if we have waiters on this message ? */ sof_ipc_tx_msg_reply_complete(sdev->ipc, msg); + return 0; } EXPORT_SYMBOL(snd_sof_ipc_reply); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 91115c82e62506..72b8870733094c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -385,7 +385,7 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset); */ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev); void snd_sof_ipc_free(struct snd_sof_dev *sdev); -void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); +int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev); void snd_sof_ipc_msgs_tx(struct snd_sof_dev *sdev); int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev, From de14e414efc5dd9738c7964d3104bada39ae1c49 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 6 Jul 2018 17:43:35 +0800 Subject: [PATCH 251/285] ASoC: SOF: Add direction param in cmd_done There are two direction now in cmd_done, one is reply of host to DSP, another is reply of DSP to host.The origin cmd_done is for reply of host to DSP. Cmd_done should be called after host has processed the reply msg from dsp. This function is for some platforms to do extra steps to make the following ipc msgs sent safely. Now the direction supported by cmd_done would be host reply to dsp or dsp reply to host. Signed-off-by: Rander Wang --- sound/soc/sof/intel/bdw.c | 2 +- sound/soc/sof/intel/cnl.c | 14 ++++++++------ sound/soc/sof/intel/hda-ipc.c | 15 +++++++++------ sound/soc/sof/intel/hda.h | 2 +- sound/soc/sof/ipc.c | 4 +++- sound/soc/sof/ops.c | 2 +- sound/soc/sof/ops.h | 5 +++-- sound/soc/sof/sof-priv.h | 5 ++++- 8 files changed, 30 insertions(+), 19 deletions(-) diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index f76463abe07fa5..7e8fab0e5ded51 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -573,7 +573,7 @@ static int bdw_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) return ret; } -static int bdw_cmd_done(struct snd_sof_dev *sdev) +static int bdw_cmd_done(struct snd_sof_dev *sdev, int dir) { /* clear BUSY bit and set DONE bit - accept new messages */ snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCD, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 10c98eb1371219..c1b902fd03feb4 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -132,13 +132,15 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) return ret; } -static int cnl_ipc_cmd_done(struct snd_sof_dev *sdev) +static int cnl_ipc_cmd_done(struct snd_sof_dev *sdev, int dir) { - /* set done bit to ack dsp the msg has been processed */ - snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, - CNL_DSP_REG_HIPCTDA, - CNL_DSP_REG_HIPCTDA_DONE, - CNL_DSP_REG_HIPCTDA_DONE); + if (dir == SOF_IPC_HOST_REPLY) { + /* set done bit to ack dsp the msg has been processed */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDA, + CNL_DSP_REG_HIPCTDA_DONE, + CNL_DSP_REG_HIPCTDA_DONE); + } return 0; } diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 357d909bfe80d6..e3e0c8fdcff682 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -34,13 +34,16 @@ #include "../ops.h" #include "hda.h" -int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev) +int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir) { - /* tell DSP cmd is done - clear busy interrupt */ - snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, - HDA_DSP_REG_HIPCT, - HDA_DSP_REG_HIPCT_BUSY, - HDA_DSP_REG_HIPCT_BUSY); + if (dir == SOF_IPC_HOST_REPLY) { + /* tell DSP cmd is done - clear busy interrupt */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCT, + HDA_DSP_REG_HIPCT_BUSY, + HDA_DSP_REG_HIPCT_BUSY); + } + return 0; } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 75f5b409d743bc..77a2315223a0da 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -473,7 +473,7 @@ int hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev, int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id); irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context); irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context); -int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev); +int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); /* * DSP Code loader. diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 6b2b1d12646f6f..52524fb1064a70 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -119,6 +119,8 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, spin_unlock_irqrestore(&sdev->ipc_lock, flags); + snd_sof_dsp_cmd_done(sdev, SOF_IPC_DSP_REPLY); + /* continue to schedule any remaining messages... */ snd_sof_ipc_msgs_tx(sdev); @@ -321,7 +323,7 @@ static void ipc_msgs_rx(struct work_struct *work) dev_dbg(sdev->dev, "ipc rx: 0x%x done\n", hdr.cmd); /* tell DSP we are done */ - snd_sof_dsp_cmd_done(sdev); + snd_sof_dsp_cmd_done(sdev, SOF_IPC_HOST_REPLY); } /* schedule work to transmit any IPC in queue */ diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 0668f64e1ef672..d1ea6a0c5f6229 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -205,6 +205,6 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); snd_sof_trace_notify_for_error(sdev); - snd_sof_dsp_cmd_done(sdev); + snd_sof_dsp_cmd_done(sdev, SOF_IPC_HOST_REPLY); } EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 340f1ccc40447f..0d6fa6ef1a3e8b 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -185,10 +185,11 @@ static inline int snd_sof_dsp_is_ready(struct snd_sof_dev *sdev) return 0; } -static inline int snd_sof_dsp_cmd_done(struct snd_sof_dev *sdev) +static inline int snd_sof_dsp_cmd_done(struct snd_sof_dev *sdev, + int dir) { if (sdev->ops->cmd_done) - return sdev->ops->cmd_done(sdev); + return sdev->ops->cmd_done(sdev, dir); else return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 72b8870733094c..c4162d3973e00a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -43,6 +43,9 @@ /* max number of FE PCMs before BEs */ #define SOF_BE_PCM_BASE 16 +#define SOF_IPC_DSP_REPLY 0 +#define SOF_IPC_HOST_REPLY 1 + /* convenience constructor for DAI driver streams */ #define SOF_DAI_STREAM(sname, scmin, scmax, srates, sfmt) \ {.stream_name = sname, .channels_min = scmin, .channels_max = scmax, \ @@ -117,7 +120,7 @@ struct snd_sof_dsp_ops { int (*get_reply)(struct snd_sof_dev *sof_dev, struct snd_sof_ipc_msg *msg); int (*is_ready)(struct snd_sof_dev *sof_dev); - int (*cmd_done)(struct snd_sof_dev *sof_dev); + int (*cmd_done)(struct snd_sof_dev *sof_dev, int dir); /* debug */ const struct snd_sof_debugfs_map *debug_map; From 3f2510724e462c36e91fa4053632d1b3218f1aba Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 6 Jul 2018 17:46:24 +0800 Subject: [PATCH 252/285] ASoC: SOF: check is_dsp_ready when sending ipc msg Refine the implementation of is_dsp_ready on APL&CNL BUSY bit means dsp is ready, DONE bit means host has processed the reply msg. The next ipc msg should check both bits to make sure both dsp and host is ready Signed-off-by: Rander Wang Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/cnl.c | 8 +++++--- sound/soc/sof/intel/hda-ipc.c | 8 +++++--- sound/soc/sof/ipc.c | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index c1b902fd03feb4..b7b7de9861b85c 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -147,10 +147,12 @@ static int cnl_ipc_cmd_done(struct snd_sof_dev *sdev, int dir) static int cnl_ipc_is_ready(struct snd_sof_dev *sdev) { - u64 val; + u64 busy, done; - val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR); - if (val & CNL_DSP_REG_HIPCIDR_BUSY) + busy = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR); + done = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA); + if ((busy & CNL_DSP_REG_HIPCIDR_BUSY) || + (done & CNL_DSP_REG_HIPCIDA_DONE)) return 0; return 1; diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index e3e0c8fdcff682..38b4111611e559 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -49,11 +49,13 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir) int hda_dsp_ipc_is_ready(struct snd_sof_dev *sdev) { - u64 val; + u64 busy, done; /* is DSP ready for next IPC command */ - val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); - if (val & HDA_DSP_REG_HIPCI_BUSY) + busy = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); + done = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE); + if ((busy & HDA_DSP_REG_HIPCI_BUSY) || + (done & HDA_DSP_REG_HIPCIE_DONE)) return 0; return 1; diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 52524fb1064a70..be6a98e9d28049 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -179,7 +179,7 @@ static void ipc_tx_next_msg(struct work_struct *work) spin_lock_irq(&sdev->ipc_lock); /* send message if HW read and message in TX list */ - if (list_empty(&ipc->tx_list)) + if (list_empty(&ipc->tx_list) || !snd_sof_dsp_is_ready(sdev)) goto out; /* sned first message in TX list */ From 67675cb51a29de649934df0da7c735e7139ac11f Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Thu, 5 Jul 2018 16:28:53 +0800 Subject: [PATCH 253/285] [SQUASHME]ASoC: SOF: cleanup for pci probing. Signed-off-by: Keyon Jie --- sound/soc/sof/sof-pci-dev.c | 47 +++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 83c71ac3418eb8..eaa04ad567933a 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -198,9 +198,17 @@ static int sof_pci_probe(struct pci_dev *pci, dev_dbg(&pci->dev, "PCI DSP detected"); + /* get ops for platform */ + ops = sof_pci_get_ops(desc); + if (!ops) { + dev_err(dev, "error: no matching PCI descriptor ops\n"); + return -ENODEV; + } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + pci_set_drvdata(pci, priv); sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL); @@ -213,14 +221,7 @@ static int sof_pci_probe(struct pci_dev *pci, ret = pci_request_regions(pci, "Audio DSP"); if (ret < 0) - return ret; - - /* get ops for platform */ - ops = sof_pci_get_ops(desc); - if (!ops) { - dev_err(dev, "error: no matching PCI descriptor ops\n"); - return -ENODEV; - } + goto disable_dev; #if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) /* force nocodec mode */ @@ -228,7 +229,7 @@ static int sof_pci_probe(struct pci_dev *pci, mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); if (ret < 0) - return ret; + goto release_regions; #else /* find machine */ mach = snd_soc_acpi_find_machine(desc->machines); @@ -239,10 +240,11 @@ static int sof_pci_probe(struct pci_dev *pci, mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops); if (ret < 0) - return ret; + goto release_regions; #else dev_err(dev, "No matching ASoC machine driver found - aborting probe\n"); - return -ENODEV; + ret = -ENODEV; + goto release_regions; #endif } #endif @@ -261,8 +263,11 @@ static int sof_pci_probe(struct pci_dev *pci, sof_pdata->pdev_mach = platform_device_register_data(dev, mach->drv_name, -1, sof_pdata, sizeof(*sof_pdata)); - if (IS_ERR(sof_pdata->pdev_mach)) - return PTR_ERR(sof_pdata->pdev_mach); + if (IS_ERR(sof_pdata->pdev_mach)) { + ret = PTR_ERR(sof_pdata->pdev_mach); + goto release_regions; + } + dev_dbg(dev, "created machine %s\n", dev_name(&sof_pdata->pdev_mach->dev)); @@ -274,8 +279,16 @@ static int sof_pci_probe(struct pci_dev *pci, platform_device_unregister(sof_pdata->pdev_mach); dev_err(dev, "error: failed to load firmware %s\n", mach->sof_fw_filename); + goto release_regions; } + return ret; + +release_regions: + pci_release_regions(pci); +disable_dev: + pci_disable_device(pci); + return ret; } @@ -289,11 +302,19 @@ static void sof_pci_remove(struct pci_dev *pci) struct sof_pci_priv *priv = pci_get_drvdata(pci); struct snd_sof_pdata *sof_pdata = priv->sof_pdata; + /* unregister machine driver */ platform_device_unregister(sof_pdata->pdev_mach); + + /* unregister sof-audio platform driver */ if (!IS_ERR_OR_NULL(priv->pdev_pcm)) platform_device_unregister(priv->pdev_pcm); + + /* release firmware */ release_firmware(sof_pdata->fw); + + /* release pci regions and disable device */ pci_release_regions(pci); + pci_disable_device(pci); } /* PCI IDs */ From 0e60d1c5ce6b54cdafbf230a256d87baceea304a Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 9 Jul 2018 13:08:56 +0800 Subject: [PATCH 254/285] [SQUASHME]ASoC: SOF: utils: fix memory leak of names during remove() Change to use devm_kstrdup() for string duplicate allocation, which will be freed together with dev free, to fix memory leak (names are not freed) during remove(). Signed-off-by: Keyon Jie --- sound/soc/sof/utils.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c index 8f2116a453526e..215ea84dfe7e68 100644 --- a/sound/soc/sof/utils.c +++ b/sound/soc/sof/utils.c @@ -26,9 +26,9 @@ int sof_bes_setup(struct device *dev, struct snd_sof_dsp_ops *ops, /* set up BE dai_links */ for (i = 0; i < link_num; i++) { snprintf(name, 32, "NoCodec-%d", i); - links[i].name = kmemdup(name, sizeof(name), GFP_KERNEL); + links[i].name = devm_kstrdup(dev, name, GFP_KERNEL); if (!links[i].name) - goto no_mem; + return -ENOMEM; links[i].id = i; links[i].no_pcm = 1; @@ -44,12 +44,6 @@ int sof_bes_setup(struct device *dev, struct snd_sof_dsp_ops *ops, card->num_links = link_num; return 0; -no_mem: - /* free allocated memories and return error */ - for (; i > 0; i--) - kfree(links[i - 1].name); - - return -ENOMEM; } EXPORT_SYMBOL(sof_bes_setup); From 675767ce6a2a7dadc0ad305915ed776622aad7a1 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Tue, 10 Jul 2018 10:51:25 +0800 Subject: [PATCH 255/285] ASoC: Intel: fix dai link for bxt_pcm512x Change cpu dai name for SSP5 and add capture capability. Signed-off-by: Pan Xiuli --- sound/soc/intel/boards/bxt_pcm512x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/bxt_pcm512x.c b/sound/soc/intel/boards/bxt_pcm512x.c index c375d0c201582e..00bd50573827b1 100644 --- a/sound/soc/intel/boards/bxt_pcm512x.c +++ b/sound/soc/intel/boards/bxt_pcm512x.c @@ -108,7 +108,7 @@ static struct snd_soc_dai_link dailink[] = { { .name = "SSP5-Codec", .id = 0, - .cpu_dai_name = "sof-audio", + .cpu_dai_name = "SSP5 Pin", .platform_name = "sof-audio", .no_pcm = 1, .codec_dai_name = "pcm512x-hifi", @@ -119,6 +119,7 @@ static struct snd_soc_dai_link dailink[] = { .be_hw_params_fixup = codec_fixup, .nonatomic = true, .dpcm_playback = 1, + .dpcm_capture = 1, }, }; From 487934868e161eef58edf44bec9bde0b49a8cc5f Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 3 Jul 2018 13:47:06 +0800 Subject: [PATCH 256/285] ASoC: SOF: process SOF_IPC_DSP_REPLY case in cmd_done on APL Set DONE bit in cmd_done function which processes msgs, rather than doing it in IRQ function. But if a msg is not in the rx list, it can only be done at IRQ function Signed-off-by: Rander Wang --- sound/soc/sof/intel/hda-ipc.c | 45 ++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 38b4111611e559..0fb3f48ef776a6 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -37,11 +37,29 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir) { if (dir == SOF_IPC_HOST_REPLY) { - /* tell DSP cmd is done - clear busy interrupt */ + /* + * tell DSP cmd is done - clear busy + * interrupt and send reply msg to dsp + */ snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT, HDA_DSP_REG_HIPCT_BUSY, HDA_DSP_REG_HIPCT_BUSY); + } else { + /* + * set DONE bit - tell DSP we have received the reply msg + * from DSP, and processed it, don't send more reply to host + */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCIE, + HDA_DSP_REG_HIPCIE_DONE, + HDA_DSP_REG_HIPCIE_DONE); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_DONE, + HDA_DSP_REG_HIPCCTL_DONE); } return 0; @@ -113,6 +131,7 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; u32 hipci, hipcie, hipct, hipcte, msg = 0, msg_ext = 0; irqreturn_t ret = IRQ_NONE; + int reply = -EINVAL; /* here we handle IPC interrupts only */ if (!(sdev->irq_status & HDA_DSP_ADSPIS_IPC)) @@ -142,19 +161,17 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) /* handle immediate reply from DSP core - ignore ROM messages */ if (msg != 0x1004000) - snd_sof_ipc_reply(sdev, msg); - - /* clear DONE bit - tell DSP we have completed the operation */ - snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, - HDA_DSP_REG_HIPCIE, - HDA_DSP_REG_HIPCIE_DONE, - HDA_DSP_REG_HIPCIE_DONE); - - /* unmask Done interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, - HDA_DSP_REG_HIPCCTL, - HDA_DSP_REG_HIPCCTL_DONE, - HDA_DSP_REG_HIPCCTL_DONE); + reply = snd_sof_ipc_reply(sdev, msg); + + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (reply) + hda_dsp_ipc_cmd_done(sdev, SOF_IPC_DSP_REPLY); ret = IRQ_HANDLED; } From e094e3ece570eaee897898eadfc7bf660cbe8d9c Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 3 Jul 2018 13:53:50 +0800 Subject: [PATCH 257/285] ASoC: SOF: process SOF_IPC_DSP_REPLY case in cmd_done on CNL Set DONE bit in cmd_done function if direction is DSP_REPLY rather than doing it in IRQ function. But if a msg is not in the rx list, it can only be done at IRQ function Signed-off-by: Rander Wang --- sound/soc/sof/intel/cnl.c | 51 ++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index b7b7de9861b85c..f2c616904cbb86 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -40,6 +40,8 @@ static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = { {"dsp", HDA_DSP_BAR, 0, 0x10000}, }; +static int cnl_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); + static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; @@ -69,20 +71,15 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) CNL_DSP_REG_HIPCCTL, CNL_DSP_REG_HIPCCTL_DONE, 0); - /* handle immediate reply from DSP core */ - snd_sof_ipc_reply(sdev, msg); - - /* clear DONE bit - tell DSP we have completed the operation */ - snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, - CNL_DSP_REG_HIPCIDA, - CNL_DSP_REG_HIPCIDA_DONE, - CNL_DSP_REG_HIPCIDA_DONE); - - /* unmask Done interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, - CNL_DSP_REG_HIPCCTL, - CNL_DSP_REG_HIPCCTL_DONE, - CNL_DSP_REG_HIPCCTL_DONE); + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (snd_sof_ipc_reply(sdev, msg)) + cnl_ipc_cmd_done(sdev, SOF_IPC_DSP_REPLY); ret = IRQ_HANDLED; } @@ -108,8 +105,10 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) snd_sof_ipc_msgs_rx(sdev); } - /* clear busy interrupt to tell dsp controller this */ - /* interrupt has been accepted, not trigger it again */ + /* + * clear busy interrupt to tell dsp controller this + * interrupt has been accepted, not trigger it again + */ snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR, CNL_DSP_REG_HIPCTDR_BUSY, @@ -135,11 +134,29 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) static int cnl_ipc_cmd_done(struct snd_sof_dev *sdev, int dir) { if (dir == SOF_IPC_HOST_REPLY) { - /* set done bit to ack dsp the msg has been processed */ + /* + * set done bit to ack dsp the msg has been + * processed and send reply msg to dsp + */ snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDA, CNL_DSP_REG_HIPCTDA_DONE, CNL_DSP_REG_HIPCTDA_DONE); + } else { + /* + * set DONE bit - tell DSP we have received the reply msg + * from DSP, and processed it, don't send more reply to host + */ + snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCIDA, + CNL_DSP_REG_HIPCIDA_DONE, + CNL_DSP_REG_HIPCIDA_DONE); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, + CNL_DSP_REG_HIPCCTL_DONE); } return 0; From d4c6f29252368657fe17b7e1af47d4f2938ff0c6 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 4 Jul 2018 15:24:21 +0800 Subject: [PATCH 258/285] ASoC: SOF: apply IPC changes to hsw (1) Set DONE bit in cmd_done function which processes msgs, rather than doing it in IRQ function. But if a msg is not in the rx list, it can only be done at IRQ function (2) Check DONE bit in hsw_is_ready function Signed-off-by: Rander Wang --- sound/soc/sof/intel/hsw.c | 49 +++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/sound/soc/sof/intel/hsw.c b/sound/soc/sof/intel/hsw.c index d81f8de3d65608..42d7fa622ff7c6 100644 --- a/sound/soc/sof/intel/hsw.c +++ b/sound/soc/sof/intel/hsw.c @@ -71,6 +71,8 @@ static const struct snd_sof_debugfs_map hsw_debugfs[] = { {"shim", HSW_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, }; +static int hsw_cmd_done(struct snd_sof_dev *sdev, int dir); + /* * Memory copy. */ @@ -363,15 +365,16 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) /* Handle Immediate reply from DSP Core */ hsw_mailbox_read(sdev, sdev->host_box.offset, &hdr, sizeof(hdr)); - snd_sof_ipc_reply(sdev, hdr); - - /* clear DONE bit - tell DSP we have completed */ - snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCX, - SHIM_IPCX_DONE, 0); - /* unmask Done interrupt */ - snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, - SHIM_IMRX_DONE, 0); + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (snd_sof_ipc_reply(sdev, hdr)) + hsw_cmd_done(sdev, SOF_IPC_DSP_REPLY); } ipcd = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCD); @@ -528,7 +531,7 @@ static int hsw_is_ready(struct snd_sof_dev *sdev) u32 val; val = snd_sof_dsp_read(sdev, HSW_DSP_BAR, SHIM_IPCX); - if (val & SHIM_IPCX_BUSY) + if ((val & SHIM_IPCX_BUSY) || (val & SHIM_IPCX_DONE)) return 0; return 1; @@ -574,16 +577,26 @@ static int hsw_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) return ret; } -static int hsw_cmd_done(struct snd_sof_dev *sdev) +static int hsw_cmd_done(struct snd_sof_dev *sdev, int dir) { - /* clear BUSY bit and set DONE bit - accept new messages */ - snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCD, - SHIM_IPCD_BUSY | SHIM_IPCD_DONE, - SHIM_IPCD_DONE); - - /* unmask busy interrupt */ - snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, - SHIM_IMRX_BUSY, 0); + if (dir == SOF_IPC_HOST_REPLY) { + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCD, + SHIM_IPCD_BUSY | SHIM_IPCD_DONE, + SHIM_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + } else { + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IPCX, + SHIM_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, HSW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } return 0; } From 06216404d0caac38ee3883606c1beb52ac015672 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 4 Jul 2018 15:28:40 +0800 Subject: [PATCH 259/285] ASoC: SOF: apply IPC changes to bdw (1) Set DONE bit in cmd_done function which processes msgs, rather than doing it in IRQ function. But if a msg is not in the rx list, it can only be done at IRQ function (2) Check DONE bit in bdw_is_ready function Signed-off-by: Rander Wang --- sound/soc/sof/intel/bdw.c | 47 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 7e8fab0e5ded51..ada4907d558187 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -70,6 +70,8 @@ static const struct snd_sof_debugfs_map bdw_debugfs[] = { {"shim", BDW_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, }; +static int bdw_cmd_done(struct snd_sof_dev *sdev, int dir); + /* * Memory copy. */ @@ -361,15 +363,16 @@ static irqreturn_t bdw_irq_thread(int irq, void *context) /* Handle Immediate reply from DSP Core */ bdw_mailbox_read(sdev, sdev->host_box.offset, &hdr, sizeof(hdr)); - snd_sof_ipc_reply(sdev, hdr); - - /* clear DONE bit - tell DSP we have completed */ - snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCX, - SHIM_IPCX_DONE, 0); - /* unmask Done interrupt */ - snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, - SHIM_IMRX_DONE, 0); + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (snd_sof_ipc_reply(sdev, hdr)) + bdw_cmd_done(sdev, SOF_IPC_DSP_REPLY); } ipcd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); @@ -526,7 +529,7 @@ static int bdw_is_ready(struct snd_sof_dev *sdev) u32 val; val = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); - if (val & SHIM_IPCX_BUSY) + if ((val & SHIM_IPCX_BUSY) || (val & SHIM_IPCX_DONE)) return 0; return 1; @@ -575,14 +578,24 @@ static int bdw_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) static int bdw_cmd_done(struct snd_sof_dev *sdev, int dir) { - /* clear BUSY bit and set DONE bit - accept new messages */ - snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCD, - SHIM_IPCD_BUSY | SHIM_IPCD_DONE, - SHIM_IPCD_DONE); - - /* unmask busy interrupt */ - snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, - SHIM_IMRX_BUSY, 0); + if (dir == SOF_IPC_HOST_REPLY) { + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCD, + SHIM_IPCD_BUSY | SHIM_IPCD_DONE, + SHIM_IPCD_DONE); + + /* unmask busy interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + } else { + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IPCX, + SHIM_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits_unlocked(sdev, BDW_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } return 0; } From d5972257ab9795fe53d3e2579bacfab43437d69a Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 4 Jul 2018 15:31:08 +0800 Subject: [PATCH 260/285] ASoC: SOF: apply IPC changes to byt (1) Set DONE bit in cmd_done function which processes msgs, rather than doing it in IRQ function. But if a msg is not in the rx list, it can only be done at IRQ function (2) Check DONE bit in byt_is_ready function Signed-off-by: Rander Wang --- sound/soc/sof/intel/byt.c | 54 ++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 9d06dd312f56d8..e2dd813e44922a 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -95,6 +95,8 @@ static const struct snd_sof_debugfs_map cht_debugfs[] = { {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE}, }; +static int byt_cmd_done(struct snd_sof_dev *sdev, int dir); + /* * Register IO */ @@ -376,16 +378,15 @@ static irqreturn_t byt_irq_thread(int irq, void *context) /* reply message from DSP */ if (ipcx & SHIM_BYT_IPCX_DONE) { - /* Handle Immediate reply from DSP Core */ - snd_sof_ipc_reply(sdev, ipcx); - - /* clear DONE bit - tell DSP we have completed */ - snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCX, - SHIM_BYT_IPCX_DONE, 0); - - /* unmask Done interrupt */ - snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, - SHIM_IMRX_DONE, 0); + /* + * handle immediate reply from DSP core. If the msg is + * found, set done bit in cmd_done which is called at the + * end of message processing function, else set it here + * because the done bit can't be set in cmd_done function + * which is triggered by msg + */ + if (snd_sof_ipc_reply(sdev, ipcx)) + byt_cmd_done(sdev, SOF_IPC_DSP_REPLY); } /* new message from DSP */ @@ -405,10 +406,11 @@ static irqreturn_t byt_irq_thread(int irq, void *context) static int byt_is_ready(struct snd_sof_dev *sdev) { - u64 imrx; + u64 imrx, ipcx; imrx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRX); - if (imrx & SHIM_IMRX_DONE) + ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX); + if ((imrx & SHIM_IMRX_DONE) || (ipcx & SHIM_BYT_IPCX_DONE)) return 0; return 1; @@ -458,17 +460,27 @@ static int byt_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) return ret; } -static int byt_cmd_done(struct snd_sof_dev *sdev) +static int byt_cmd_done(struct snd_sof_dev *sdev, int dir) { - /* clear BUSY bit and set DONE bit - accept new messages */ - snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCD, - SHIM_BYT_IPCD_BUSY | - SHIM_BYT_IPCD_DONE, - SHIM_BYT_IPCD_DONE); + if (dir == SOF_IPC_HOST_REPLY) { + /* clear BUSY bit and set DONE bit - accept new messages */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCD, + SHIM_BYT_IPCD_BUSY | + SHIM_BYT_IPCD_DONE, + SHIM_BYT_IPCD_DONE); - /* unmask busy interrupt */ - snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, - SHIM_IMRX_BUSY, 0); + /* unmask busy interrupt */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_BUSY, 0); + } else { + /* clear DONE bit - tell DSP we have completed */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCX, + SHIM_BYT_IPCX_DONE, 0); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX, + SHIM_IMRX_DONE, 0); + } return 0; } From ad3c0e18159a0f152fd210adb5aa0e0a850fafcf Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Thu, 7 Jun 2018 09:09:33 +0800 Subject: [PATCH 261/285] ASoC: SOF: enable msi for sof hda audio This patch tries to enable msi for sof hda audio driver. If it fails (not supported by HW/BIOS), it will use the legacy interrupt mode. Signed-off-by: Libin Yang --- sound/soc/sof/intel/hda.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 781a2f4a135ae5..4d6b42028d1ed7 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -446,20 +446,35 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); - dev_dbg(sdev->dev, "using PCI IRQ %d\n", pci->irq); + /* + * register our IRQ + * let's try to enable msi firstly + * if it fails, use legacy interrupt mode + * TODO: support interrupt mode selection with kernel parameter + * support msi multiple vectors + */ + ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI); + if (ret < 0) { + dev_info(sdev->dev, "use legacy interrupt mode\n"); + sdev->hda->irq = pci->irq; + sdev->ipc_irq = pci->irq; + } else { + dev_info(sdev->dev, "use msi interrupt mode\n"); + sdev->hda->irq = pci_irq_vector(pci, 0); + /* ipc irq number is the same of hda irq */ + sdev->ipc_irq = sdev->hda->irq; + } - /* register our IRQ */ - ret = request_threaded_irq(pci->irq, hda_dsp_stream_interrupt, + dev_dbg(sdev->dev, "using HDA IRQ %d\n", sdev->hda->irq); + ret = request_threaded_irq(sdev->hda->irq, hda_dsp_stream_interrupt, hda_dsp_stream_threaded_handler, - IRQF_SHARED, "AudioHDA", sdev); + IRQF_SHARED, "AudioHDA", sdev); if (ret < 0) { dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n", - sdev->ipc_irq); + sdev->hda->irq); goto stream_err; } - sdev->hda->irq = pci->irq; - sdev->ipc_irq = pci->irq; dev_dbg(sdev->dev, "using IPC IRQ %d\n", sdev->ipc_irq); ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_ipc_irq_handler, chip->ops->irq_thread, IRQF_SHARED, @@ -494,8 +509,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) return 0; irq_err: - free_irq(pci->irq, sdev); + free_irq(sdev->hda->irq, sdev); stream_err: + pci_free_irq_vectors(pci); hda_dsp_stream_free(sdev); err: /* disable DSP */ @@ -506,6 +522,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) int hda_dsp_remove(struct snd_sof_dev *sdev) { + struct pci_dev *pci = sdev->pci; const struct sof_intel_dsp_desc *chip = sdev->hda->desc; /* disable DSP IRQ */ @@ -526,6 +543,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) free_irq(sdev->ipc_irq, sdev); free_irq(sdev->pci->irq, sdev); + pci_free_irq_vectors(pci); hda_dsp_stream_free(sdev); return 0; From e7823fc24c050b3d3f0124c591af9cd9e3061e78 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 13 Jul 2018 13:27:23 +0800 Subject: [PATCH 262/285] ASoC: SOF: refine and unify sof_xxx_priv structs All sof_pci_priv, sof_acpi_priv and sof_spi_priv are exactly same structs, here unify them and put it to sof-priv.h. Signed-off-by: Keyon Jie --- sound/soc/sof/sof-acpi-dev.c | 11 +++-------- sound/soc/sof/sof-pci-dev.c | 11 +++-------- sound/soc/sof/sof-priv.h | 9 +++++++++ sound/soc/sof/sof-spi-dev.c | 11 +++-------- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index fec3b012ae7191..9ada41f59917ad 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -134,14 +134,9 @@ static struct platform_device * return pdev; } -struct sof_acpi_priv { - struct snd_sof_pdata *sof_pdata; - struct platform_device *pdev_pcm; -}; - static void sof_acpi_fw_cb(const struct firmware *fw, void *context) { - struct sof_acpi_priv *priv = context; + struct sof_platform_priv *priv = context; struct snd_sof_pdata *sof_pdata = priv->sof_pdata; const struct snd_soc_acpi_mach *mach = sof_pdata->machine; struct device *dev = &sof_pdata->pdev->dev; @@ -209,7 +204,7 @@ static int sof_acpi_probe(struct platform_device *pdev) const struct sof_dev_desc *desc; struct snd_soc_acpi_mach *mach; struct snd_sof_pdata *sof_pdata; - struct sof_acpi_priv *priv; + struct sof_platform_priv *priv; struct snd_sof_dsp_ops *ops; struct platform_device *(*new_mach_data)(struct snd_sof_pdata *pdata); int ret = 0; @@ -315,7 +310,7 @@ static void sof_acpi_shutdown(struct platform_device *pdev) static int sof_acpi_remove(struct platform_device *pdev) { - struct sof_acpi_priv *priv = dev_get_drvdata(&pdev->dev); + struct sof_platform_priv *priv = dev_get_drvdata(&pdev->dev); struct snd_sof_pdata *sof_pdata = priv->sof_pdata; platform_device_unregister(sof_pdata->pdev_mach); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index eaa04ad567933a..470d7d578e6245 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -114,14 +114,9 @@ static struct sof_dev_desc kbl_desc = { }; #endif -struct sof_pci_priv { - struct snd_sof_pdata *sof_pdata; - struct platform_device *pdev_pcm; -}; - static void sof_pci_fw_cb(const struct firmware *fw, void *context) { - struct sof_pci_priv *priv = context; + struct sof_platform_priv *priv = context; struct snd_sof_pdata *sof_pdata = priv->sof_pdata; const struct snd_soc_acpi_mach *mach = sof_pdata->machine; struct device *dev = sof_pdata->dev; @@ -192,7 +187,7 @@ static int sof_pci_probe(struct pci_dev *pci, (const struct sof_dev_desc *)pci_id->driver_data; struct snd_soc_acpi_mach *mach; struct snd_sof_pdata *sof_pdata; - struct sof_pci_priv *priv; + struct sof_platform_priv *priv; struct snd_sof_dsp_ops *ops; int ret = 0; @@ -299,7 +294,7 @@ static void sof_pci_shutdown(struct pci_dev *pci) static void sof_pci_remove(struct pci_dev *pci) { - struct sof_pci_priv *priv = pci_get_drvdata(pci); + struct sof_platform_priv *priv = pci_get_drvdata(pci); struct snd_sof_pdata *sof_pdata = priv->sof_pdata; /* unregister machine driver */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index c4162d3973e00a..5def46b92f1957 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -353,6 +353,15 @@ struct snd_sof_dev { void *private; /* core does not touch this */ }; +/* + * SOF platform private struct used as drvdata of + * platform dev (e.g. pci/acpi/spi...) drvdata. + */ +struct sof_platform_priv { + struct snd_sof_pdata *sof_pdata; + struct platform_device *pdev_pcm; +}; + /* * Device Level. */ diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index b3811db6d74650..45860bfb1741c8 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -19,14 +19,9 @@ #include #include "sof-priv.h" -struct sof_spi_priv { - struct snd_sof_pdata *sof_pdata; - struct platform_device *pdev_pcm; -}; - static void sof_spi_fw_cb(const struct firmware *fw, void *context) { - struct sof_spi_priv *priv = context; + struct sof_platform_priv *priv = context; struct snd_sof_pdata *sof_pdata = priv->sof_pdata; const struct snd_sof_machine *mach = sof_pdata->machine; struct device *dev = sof_pdata->dev; @@ -61,7 +56,7 @@ static int sof_spi_probe(struct spi_device *spi) const struct snd_sof_machine *mach; struct snd_sof_machine *m; struct snd_sof_pdata *sof_pdata; - struct sof_spi_priv *priv; + struct sof_platform_priv *priv; int ret = 0; dev_dbg(&spi->dev, "SPI DSP detected"); @@ -117,7 +112,7 @@ static int sof_spi_probe(struct spi_device *spi) static int sof_spi_remove(struct spi_device *spi) { - struct sof_spi_priv *priv = spi_get_drvdata(spi); + struct sof_platform_priv *priv = spi_get_drvdata(spi); struct snd_sof_pdata *sof_pdata = priv->sof_pdata; platform_device_unregister(sof_pdata->pdev_mach); From 9b33dfacf8f0c9fffc22b1cdd5556faad31ed133 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 16 Jul 2018 13:05:31 +0800 Subject: [PATCH 263/285] ASoC: SOF: remove superfluous parent device pointers of snd_sof_pdata We had pointers for parent device of snd_sof_pdata, pci for pci_dev, pdev for platform_device, and spi for spi_device (intended to but not added yet). All of these can be retrieved from parent dev via container_of(), so they are actually superfluous. Here remove these pointers to kinds of parent devices, add an enum sof_device_type and simplify the initialization of snd_sof_dev->parent correspondingly. Signed-off-by: Keyon Jie --- include/sound/sof.h | 12 +++++++++--- sound/soc/sof/core.c | 11 +++-------- sound/soc/sof/sof-acpi-dev.c | 7 ++++--- sound/soc/sof/sof-pci-dev.c | 2 +- sound/soc/sof/sof-spi-dev.c | 2 +- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index e522e13e7a18d7..835d0ae4770cf0 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -23,6 +23,13 @@ struct snd_sof_dsp_ops; +/* SOF probe type */ +enum sof_device_type { + SOF_DEVICE_PCI = 0, + SOF_DEVICE_APCI, + SOF_DEVICE_SPI +}; + /* * SOF Platform data. */ @@ -32,10 +39,9 @@ struct snd_sof_pdata { const char *drv_name; const char *name; - /* parent devices */ + /* parent device */ struct device *dev; - struct pci_dev *pci; - struct platform_device *pdev; + enum sof_device_type type; /* descriptor */ const struct sof_dev_desc *desc; diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index ee76082cd891a9..5aad175d6e2556 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -239,14 +239,9 @@ static int sof_probe(struct platform_device *pdev) /* initialize sof device */ sdev->dev = &pdev->dev; - if (plat_data->pci) { - sdev->pci = plat_data->pci; - sdev->parent = &plat_data->pci->dev; - } else if (plat_data->pdev) { - sdev->parent = &plat_data->pdev->dev; - } else { - sdev->parent = plat_data->dev; - } + sdev->parent = plat_data->dev; + if (plat_data->type == SOF_DEVICE_PCI) + sdev->pci = container_of(plat_data->dev, struct pci_dev, dev); sdev->ops = plat_data->machine->pdata; sdev->pdata = plat_data; diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index 9ada41f59917ad..dd268c1c1225d6 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -121,7 +121,7 @@ static struct platform_device * mfld_new_mach_data(struct snd_sof_pdata *sof_pdata) { struct snd_soc_acpi_mach pmach; - struct device *dev = &sof_pdata->pdev->dev; + struct device *dev = sof_pdata->dev; const struct snd_soc_acpi_mach *mach = sof_pdata->machine; struct platform_device *pdev = NULL; @@ -139,7 +139,7 @@ static void sof_acpi_fw_cb(const struct firmware *fw, void *context) struct sof_platform_priv *priv = context; struct snd_sof_pdata *sof_pdata = priv->sof_pdata; const struct snd_soc_acpi_mach *mach = sof_pdata->machine; - struct device *dev = &sof_pdata->pdev->dev; + struct device *dev = sof_pdata->dev; sof_pdata->fw = fw; if (!fw) { @@ -273,7 +273,8 @@ static int sof_acpi_probe(struct platform_device *pdev) */ sof_pdata->desc = desc; priv->sof_pdata = sof_pdata; - sof_pdata->pdev = pdev; + sof_pdata->dev = &pdev->dev; + sof_pdata->type = SOF_DEVICE_APCI; dev_set_drvdata(&pdev->dev, priv); /* do we need to generate any machine plat data ? */ diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 470d7d578e6245..152a3d36b69ad3 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -251,8 +251,8 @@ static int sof_pci_probe(struct pci_dev *pci, sof_pdata->machine = mach; sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; priv->sof_pdata = sof_pdata; - sof_pdata->pci = pci; sof_pdata->dev = &pci->dev; + sof_pdata->type = SOF_DEVICE_PCI; /* register machine driver */ sof_pdata->pdev_mach = diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index 45860bfb1741c8..57928d1442c96b 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -89,8 +89,8 @@ static int sof_spi_probe(struct spi_device *spi) sof_pdata->machine = mach; sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; priv->sof_pdata = sof_pdata; - sof_pdata->spi = spi; sof_pdata->dev = dev; + sof_pdata->type = SOF_DEVICE_SPI; /* register machine driver */ sof_pdata->pdev_mach = From 0287ea6b1515f17899e427fe10aed7b18215d53a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Jul 2018 13:17:37 -0500 Subject: [PATCH 264/285] [SQUASHME] ASoC: SOF: remove unused SOF_PLATFORM from Makefile/Kconfig This option points to non-existent files, remove. Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/Kconfig | 3 --- sound/soc/sof/Makefile | 2 -- 2 files changed, 5 deletions(-) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index cec2ea91df14dc..ba54d82fd7da6b 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -4,9 +4,6 @@ config SND_SOC_SOF_PCI config SND_SOC_SOF_ACPI tristate -config SND_SOC_SOF_PLATFORM - tristate - config SND_SOC_SOF tristate "Sound Open Firmware Support" default m diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index b3ba39d61b2a9e..b8f6c67fbb7b70 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -8,7 +8,6 @@ snd-sof-spi-objs := hw-spi.o snd-sof-pci-objs := sof-pci-dev.o snd-sof-acpi-objs := sof-acpi-dev.o -snd-sof-platform-objs := sof-platform-dev.o snd-sof-nocodec-objs := nocodec.o obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o @@ -17,7 +16,6 @@ obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o obj-$(CONFIG_SND_SOC_SOF_ACPI) += sof-acpi-dev.o obj-$(CONFIG_SND_SOC_SOF_PCI) += sof-pci-dev.o obj-$(CONFIG_SND_SOC_SOF_SPI) += sof-spi-dev.o -obj-$(SND_SOC_SOF_PLATFORM) += sof-platform-dev.o obj-$(CONFIG_SND_SOC_SOF_SPIDSP) += snd-sof-spi.o From 26c56d760e2a05cb4d30e7453a82493e6582133c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Jul 2018 13:24:13 -0500 Subject: [PATCH 265/285] [SQUASHME] ASoC: SOF: default should not be module Not sure why this was added but this is not quite right Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index ba54d82fd7da6b..322cf65c51ff57 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -6,7 +6,6 @@ config SND_SOC_SOF_ACPI config SND_SOC_SOF tristate "Sound Open Firmware Support" - default m select SND_SOC_TOPOLOGY select SND_SOC_COMPRESS help From d930626ac502a3dd3edb8182ab950d1bc1a2945b Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:32 +0800 Subject: [PATCH 266/285] ALSA: hdac: ext: add wait for codec to respond after link reset As per HDA spec section 4.3 - Codec Discovery, the software shall wait for atleast 521usec for codec to respond after link reset. With the multi-link capability each link is turned ON/OFF individually. Link controller drives reset signal when it is turned ON. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- sound/hda/ext/hdac_ext_controller.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index 84f3b81687164d..f83d5e3c426204 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -274,6 +274,15 @@ int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus, } ret = snd_hdac_ext_bus_link_power_up(link); + + /* + * wait for 521usec for codec to report status + * HDA spec section 4.3 - Codec Discovery + */ + udelay(521); + bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); + dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); + snd_hdac_chip_writew(bus, STATESTS, bus->codec_mask); } mutex_unlock(&ebus->lock); From 5287738b37b79f68831f8227f6874cad9ae5fa07 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:33 +0800 Subject: [PATCH 267/285] ALSA: hdac: Remove usage of struct hdac_ext_device and use hdac_device instead This patch removes the hdac_ext_device structure. The legacy and enhanced HDaudio capabilities can be handled in a backward-compatible way without separate definitions. Follow-up patches in this series handle the bus and driver definitions. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- include/sound/hdaudio_ext.h | 36 +--- sound/hda/ext/hdac_ext_bus.c | 25 +-- sound/soc/codecs/hdac_hdmi.c | 396 +++++++++++++++++------------------ 3 files changed, 204 insertions(+), 253 deletions(-) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 9c14e21dda85cf..c1a5ad0e6e3983 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -181,48 +181,20 @@ struct hda_dai_map { u32 maxbps; }; -#define HDA_MAX_NIDS 16 - -/** - * struct hdac_ext_device - HDAC Ext device - * - * @hdac: hdac core device - * @nid_list - the dai map which matches the dai-name with the nid - * @map_cur_idx - the idx in use in dai_map - * @ops - the hda codec ops common to all codec drivers - * @pvt_data - private data, for asoc contains asoc codec object - */ -struct hdac_ext_device { - struct hdac_device hdev; - struct hdac_ext_bus *ebus; - - /* soc-dai to nid map */ - struct hda_dai_map nid_list[HDA_MAX_NIDS]; - unsigned int map_cur_idx; - - /* codec ops */ - struct hdac_ext_codec_ops ops; - - struct snd_card *card; - void *scodec; - void *private_data; -}; - struct hdac_ext_dma_params { u32 format; u8 stream_tag; }; -#define to_ehdac_device(dev) (container_of((dev), \ - struct hdac_ext_device, hdev)) + /* * HD-audio codec base driver */ struct hdac_ext_driver { struct hdac_driver hdac; - int (*probe)(struct hdac_ext_device *dev); - int (*remove)(struct hdac_ext_device *dev); - void (*shutdown)(struct hdac_ext_device *dev); + int (*probe)(struct hdac_device *dev); + int (*remove)(struct hdac_device *dev); + void (*shutdown)(struct hdac_device *dev); }; int snd_hda_ext_driver_register(struct hdac_ext_driver *drv); diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 0daf31383084d2..0e4823fdd41197 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -137,17 +137,16 @@ static void default_release(struct device *dev) */ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) { - struct hdac_ext_device *edev; struct hdac_device *hdev = NULL; struct hdac_bus *bus = ebus_to_hbus(ebus); char name[15]; int ret; - edev = kzalloc(sizeof(*edev), GFP_KERNEL); - if (!edev) + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (!hdev) return -ENOMEM; - hdev = &edev->hdev; - edev->ebus = ebus; + + hdev->bus = bus; snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); @@ -176,10 +175,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); */ void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev) { - struct hdac_ext_device *edev = to_ehdac_device(hdev); - snd_hdac_device_exit(hdev); - kfree(edev); + kfree(hdev); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); @@ -212,27 +209,25 @@ static inline struct hdac_ext_driver *get_edrv(struct device *dev) return edrv; } -static inline struct hdac_ext_device *get_edev(struct device *dev) +static inline struct hdac_device *get_hdev(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); - struct hdac_ext_device *edev = to_ehdac_device(hdev); - - return edev; + return hdev; } static int hda_ext_drv_probe(struct device *dev) { - return (get_edrv(dev))->probe(get_edev(dev)); + return (get_edrv(dev))->probe(get_hdev(dev)); } static int hdac_ext_drv_remove(struct device *dev) { - return (get_edrv(dev))->remove(get_edev(dev)); + return (get_edrv(dev))->remove(get_hdev(dev)); } static void hdac_ext_drv_shutdown(struct device *dev) { - return (get_edrv(dev))->shutdown(get_edev(dev)); + return (get_edrv(dev))->shutdown(get_hdev(dev)); } /** diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 84f7a7a36e4b56..f1e235817a6596 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -85,7 +85,7 @@ struct hdac_hdmi_pin { bool mst_capable; struct hdac_hdmi_port *ports; int num_ports; - struct hdac_ext_device *edev; + struct hdac_device *hdev; }; struct hdac_hdmi_port { @@ -126,6 +126,9 @@ struct hdac_hdmi_drv_data { }; struct hdac_hdmi_priv { + struct hdac_device *hdev; + struct snd_soc_component *component; + struct snd_card *card; struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS]; struct list_head pin_list; struct list_head cvt_list; @@ -139,7 +142,7 @@ struct hdac_hdmi_priv { struct snd_soc_dai_driver *dai_drv; }; -#define hdev_to_hdmi_priv(_hdev) ((to_ehdac_device(_hdev))->private_data) +#define hdev_to_hdmi_priv(_hdev) dev_get_drvdata(&(_hdev)->dev) static struct hdac_hdmi_pcm * hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi, @@ -158,7 +161,7 @@ hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi, static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port, bool is_connect) { - struct hdac_ext_device *edev = port->pin->edev; + struct hdac_device *hdev = port->pin->hdev; if (is_connect) snd_soc_dapm_enable_pin(port->dapm, port->jack_pin); @@ -172,7 +175,7 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, * ports. */ if (pcm->jack_event == 0) { - dev_dbg(&edev->hdev.dev, + dev_dbg(&hdev->dev, "jack report for pcm=%d\n", pcm->pcm_id); snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT, @@ -198,19 +201,18 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, /* * Get the no devices that can be connected to a port on the Pin widget. */ -static int hdac_hdmi_get_port_len(struct hdac_ext_device *edev, hda_nid_t nid) +static int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid) { unsigned int caps; unsigned int type, param; - caps = get_wcaps(&edev->hdev, nid); + caps = get_wcaps(hdev, nid); type = get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN)) return 0; - param = snd_hdac_read_parm_uncached(&edev->hdev, nid, - AC_PAR_DEVLIST_LEN); + param = snd_hdac_read_parm_uncached(hdev, nid, AC_PAR_DEVLIST_LEN); if (param == -1) return param; @@ -222,10 +224,10 @@ static int hdac_hdmi_get_port_len(struct hdac_ext_device *edev, hda_nid_t nid) * id selected on the pin. Return 0 means the first port entry * is selected or MST is not supported. */ -static int hdac_hdmi_port_select_get(struct hdac_ext_device *edev, +static int hdac_hdmi_port_select_get(struct hdac_device *hdev, struct hdac_hdmi_port *port) { - return snd_hdac_codec_read(&edev->hdev, port->pin->nid, + return snd_hdac_codec_read(hdev, port->pin->nid, 0, AC_VERB_GET_DEVICE_SEL, 0); } @@ -233,7 +235,7 @@ static int hdac_hdmi_port_select_get(struct hdac_ext_device *edev, * Sets the selected port entry for the configuring Pin widget verb. * returns error if port set is not equal to port get otherwise success */ -static int hdac_hdmi_port_select_set(struct hdac_ext_device *edev, +static int hdac_hdmi_port_select_set(struct hdac_device *hdev, struct hdac_hdmi_port *port) { int num_ports; @@ -242,8 +244,7 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *edev, return 0; /* AC_PAR_DEVLIST_LEN is 0 based. */ - num_ports = hdac_hdmi_get_port_len(edev, port->pin->nid); - + num_ports = hdac_hdmi_get_port_len(hdev, port->pin->nid); if (num_ports < 0) return -EIO; /* @@ -253,13 +254,13 @@ static int hdac_hdmi_port_select_set(struct hdac_ext_device *edev, if (num_ports + 1 < port->id) return 0; - snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_DEVICE_SEL, port->id); - if (port->id != hdac_hdmi_port_select_get(edev, port)) + if (port->id != hdac_hdmi_port_select_get(hdev, port)) return -EIO; - dev_dbg(&edev->hdev.dev, "Selected the port=%d\n", port->id); + dev_dbg(&hdev->dev, "Selected the port=%d\n", port->id); return 0; } @@ -277,13 +278,6 @@ static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi, return NULL; } -static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) -{ - struct hdac_device *hdev = dev_to_hdac_dev(dev); - - return to_ehdac_device(hdev); -} - static unsigned int sad_format(const u8 *sad) { return ((sad[0] >> 0x3) & 0x1f); @@ -324,15 +318,13 @@ static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime, } static void -hdac_hdmi_set_dip_index(struct hdac_ext_device *edev, hda_nid_t pin_nid, +hdac_hdmi_set_dip_index(struct hdac_device *hdev, hda_nid_t pin_nid, int packet_index, int byte_index) { int val; val = (packet_index << 5) | (byte_index & 0x1f); - - snd_hdac_codec_write(&edev->hdev, pin_nid, 0, - AC_VERB_SET_HDMI_DIP_INDEX, val); + snd_hdac_codec_write(hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); } struct dp_audio_infoframe { @@ -347,14 +339,14 @@ struct dp_audio_infoframe { u8 LFEPBL01_LSV36_DM_INH7; }; -static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev, +static int hdac_hdmi_setup_audio_infoframe(struct hdac_device *hdev, struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port) { uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; struct hdmi_audio_infoframe frame; struct hdac_hdmi_pin *pin = port->pin; struct dp_audio_infoframe dp_ai; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_cvt *cvt = pcm->cvt; u8 *dip; int ret; @@ -363,11 +355,11 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev, u8 conn_type; int channels, ca; - ca = snd_hdac_channel_allocation(&edev->hdev, port->eld.info.spk_alloc, + ca = snd_hdac_channel_allocation(hdev, port->eld.info.spk_alloc, pcm->channels, pcm->chmap_set, true, pcm->chmap); channels = snd_hdac_get_active_channels(ca); - hdmi->chmap.ops.set_channel_count(&edev->hdev, cvt->nid, channels); + hdmi->chmap.ops.set_channel_count(hdev, cvt->nid, channels); snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, pcm->channels, pcm->chmap, pcm->chmap_set); @@ -400,32 +392,31 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *edev, break; default: - dev_err(&edev->hdev.dev, "Invalid connection type: %d\n", - conn_type); + dev_err(&hdev->dev, "Invalid connection type: %d\n", conn_type); return -EIO; } /* stop infoframe transmission */ - hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0); - snd_hdac_codec_write(&edev->hdev, pin->nid, 0, + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); /* Fill infoframe. Index auto-incremented */ - hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0); + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); if (conn_type == DRM_ELD_CONN_TYPE_HDMI) { for (i = 0; i < sizeof(buffer); i++) - snd_hdac_codec_write(&edev->hdev, pin->nid, 0, + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_DATA, buffer[i]); } else { for (i = 0; i < sizeof(dp_ai); i++) - snd_hdac_codec_write(&edev->hdev, pin->nid, 0, + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_DATA, dip[i]); } /* Start infoframe */ - hdac_hdmi_set_dip_index(edev, pin->nid, 0x0, 0x0); - snd_hdac_codec_write(&edev->hdev, pin->nid, 0, + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(hdev, pin->nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST); return 0; @@ -435,12 +426,12 @@ static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { - struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdev.dev, "%s: strm_tag: %d\n", __func__, tx_mask); + dev_dbg(&hdev->dev, "%s: strm_tag: %d\n", __func__, tx_mask); dai_map = &hdmi->dai_map[dai->id]; @@ -455,8 +446,8 @@ static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai, static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai) { - struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_port *port; struct hdac_hdmi_pcm *pcm; @@ -469,7 +460,7 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, return -ENODEV; if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) { - dev_err(&edev->hdev.dev, + dev_err(&hdev->dev, "device is not configured for this pin:port%d:%d\n", port->pin->nid, port->id); return -ENODEV; @@ -489,28 +480,28 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, return 0; } -static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *edev, +static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev, struct hdac_hdmi_pin *pin, struct hdac_hdmi_port *port) { - if (!(get_wcaps(&edev->hdev, pin->nid) & AC_WCAP_CONN_LIST)) { - dev_warn(&edev->hdev.dev, + if (!(get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) { + dev_warn(&hdev->dev, "HDMI: pin %d wcaps %#x does not support connection list\n", - pin->nid, get_wcaps(&edev->hdev, pin->nid)); + pin->nid, get_wcaps(hdev, pin->nid)); return -EINVAL; } - if (hdac_hdmi_port_select_set(edev, port) < 0) + if (hdac_hdmi_port_select_set(hdev, port) < 0) return -EIO; - port->num_mux_nids = snd_hdac_get_connections(&edev->hdev, pin->nid, + port->num_mux_nids = snd_hdac_get_connections(hdev, pin->nid, port->mux_nids, HDA_MAX_CONNECTIONS); if (port->num_mux_nids == 0) - dev_warn(&edev->hdev.dev, + dev_warn(&hdev->dev, "No connections found for pin:port %d:%d\n", pin->nid, port->id); - dev_dbg(&edev->hdev.dev, "num_mux_nids %d for pin:port %d:%d\n", + dev_dbg(&hdev->dev, "num_mux_nids %d for pin:port %d:%d\n", port->num_mux_nids, pin->nid, port->id); return port->num_mux_nids; @@ -526,7 +517,7 @@ static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *edev, * connected. */ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( - struct hdac_ext_device *edev, + struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, struct hdac_hdmi_cvt *cvt) { @@ -541,7 +532,7 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( list_for_each_entry(port, &pcm->port_list, head) { mutex_lock(&pcm->lock); - ret = hdac_hdmi_query_port_connlist(edev, + ret = hdac_hdmi_query_port_connlist(hdev, port->pin, port); mutex_unlock(&pcm->lock); if (ret < 0) @@ -568,8 +559,8 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_cvt *cvt; struct hdac_hdmi_port *port; @@ -578,7 +569,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, dai_map = &hdmi->dai_map[dai->id]; cvt = dai_map->cvt; - port = hdac_hdmi_get_port_from_cvt(edev, hdmi, cvt); + port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt); /* * To make PA and other userland happy. @@ -589,7 +580,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) { - dev_warn(&edev->hdev.dev, + dev_warn(&hdev->dev, "Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n", port->eld.monitor_present, port->eld.eld_valid, port->pin->nid, port->id); @@ -611,8 +602,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_pcm *pcm; @@ -695,10 +685,10 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, route->connected = handler; } -static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, +static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_device *hdev, struct hdac_hdmi_port *port) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = NULL; struct hdac_hdmi_port *p; @@ -715,33 +705,32 @@ static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, return NULL; } -static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev, +static void hdac_hdmi_set_power_state(struct hdac_device *hdev, hda_nid_t nid, unsigned int pwr_state) { int count; unsigned int state; - if (get_wcaps(&edev->hdev, nid) & AC_WCAP_POWER) { - if (!snd_hdac_check_power_state(&edev->hdev, nid, pwr_state)) { + if (get_wcaps(hdev, nid) & AC_WCAP_POWER) { + if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) { for (count = 0; count < 10; count++) { - snd_hdac_codec_read(&edev->hdev, nid, 0, + snd_hdac_codec_read(hdev, nid, 0, AC_VERB_SET_POWER_STATE, pwr_state); - state = snd_hdac_sync_power_state(&edev->hdev, + state = snd_hdac_sync_power_state(hdev, nid, pwr_state); if (!(state & AC_PWRST_ERROR)) break; } } - } } -static void hdac_hdmi_set_amp(struct hdac_ext_device *edev, +static void hdac_hdmi_set_amp(struct hdac_device *hdev, hda_nid_t nid, int val) { - if (get_wcaps(&edev->hdev, nid) & AC_WCAP_OUT_AMP) - snd_hdac_codec_write(&edev->hdev, nid, 0, + if (get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP) + snd_hdac_codec_write(hdev, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); } @@ -750,40 +739,40 @@ static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kc, int event) { struct hdac_hdmi_port *port = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); + struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n", + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", __func__, w->name, event); - pcm = hdac_hdmi_get_pcm(edev, port); + pcm = hdac_hdmi_get_pcm(hdev, port); if (!pcm) return -EIO; /* set the device if pin is mst_capable */ - if (hdac_hdmi_port_select_set(edev, port) < 0) + if (hdac_hdmi_port_select_set(hdev, port) < 0) return -EIO; switch (event) { case SND_SOC_DAPM_PRE_PMU: - hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D0); + hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D0); /* Enable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_UNMUTE); + hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_UNMUTE); - return hdac_hdmi_setup_audio_infoframe(edev, pcm, port); + return hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); case SND_SOC_DAPM_POST_PMD: - hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_MUTE); + hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_MUTE); /* Disable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D3); + hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D3); break; } @@ -795,11 +784,11 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kc, int event) { struct hdac_hdmi_cvt *cvt = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; - dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n", + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", __func__, w->name, event); pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt); @@ -808,29 +797,29 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D0); + hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D0); /* Enable transmission */ - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_DIGI_CONVERT_1, 1); /* Category Code (CC) to zero */ - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0); - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag); - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_STREAM_FORMAT, pcm->format); break; case SND_SOC_DAPM_POST_PMD: - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - snd_hdac_codec_write(&edev->hdev, cvt->nid, 0, + snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); - hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D3); + hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D3); break; } @@ -842,10 +831,10 @@ static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kc, int event) { struct hdac_hdmi_port *port = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); + struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); int mux_idx; - dev_dbg(&edev->hdev.dev, "%s: widget: %s event: %x\n", + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", __func__, w->name, event); if (!kc) @@ -854,11 +843,11 @@ static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, mux_idx = dapm_kcontrol_get_value(kc); /* set the device if pin is mst_capable */ - if (hdac_hdmi_port_select_set(edev, port) < 0) + if (hdac_hdmi_port_select_set(hdev, port) < 0) return -EIO; if (mux_idx > 0) { - snd_hdac_codec_write(&edev->hdev, port->pin->nid, 0, + snd_hdac_codec_write(hdev, port->pin->nid, 0, AC_VERB_SET_CONNECT_SEL, (mux_idx - 1)); } @@ -877,8 +866,8 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_context *dapm = w->dapm; struct hdac_hdmi_port *port = w->priv; - struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = NULL; const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; @@ -931,12 +920,12 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, * care of selecting the right one and leaving all other inputs selected to * "NONE" */ -static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, +static int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev, struct hdac_hdmi_port *port, struct snd_soc_dapm_widget *widget, const char *widget_name) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin = port->pin; struct snd_kcontrol_new *kc; struct hdac_hdmi_cvt *cvt; @@ -948,17 +937,17 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, int i = 0; int num_items = hdmi->num_cvt + 1; - kc = devm_kzalloc(&edev->hdev.dev, sizeof(*kc), GFP_KERNEL); + kc = devm_kzalloc(&hdev->dev, sizeof(*kc), GFP_KERNEL); if (!kc) return -ENOMEM; - se = devm_kzalloc(&edev->hdev.dev, sizeof(*se), GFP_KERNEL); + se = devm_kzalloc(&hdev->dev, sizeof(*se), GFP_KERNEL); if (!se) return -ENOMEM; snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input", pin->nid, port->id); - kc->name = devm_kstrdup(&edev->hdev.dev, kc_name, GFP_KERNEL); + kc->name = devm_kstrdup(&hdev->dev, kc_name, GFP_KERNEL); if (!kc->name) return -ENOMEM; @@ -976,35 +965,35 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, se->mask = roundup_pow_of_two(se->items) - 1; sprintf(mux_items, "NONE"); - items[i] = devm_kstrdup(&edev->hdev.dev, mux_items, GFP_KERNEL); + items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL); if (!items[i]) return -ENOMEM; list_for_each_entry(cvt, &hdmi->cvt_list, head) { i++; sprintf(mux_items, "cvt %d", cvt->nid); - items[i] = devm_kstrdup(&edev->hdev.dev, mux_items, GFP_KERNEL); + items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL); if (!items[i]) return -ENOMEM; } - se->texts = devm_kmemdup(&edev->hdev.dev, items, + se->texts = devm_kmemdup(&hdev->dev, items, (num_items * sizeof(char *)), GFP_KERNEL); if (!se->texts) return -ENOMEM; - return hdac_hdmi_fill_widget_info(&edev->hdev.dev, widget, + return hdac_hdmi_fill_widget_info(&hdev->dev, widget, snd_soc_dapm_mux, port, widget_name, NULL, kc, 1, hdac_hdmi_pin_mux_widget_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG); } /* Add cvt <- input <- mux route map */ -static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev, +static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_device *hdev, struct snd_soc_dapm_widget *widgets, struct snd_soc_dapm_route *route, int rindex) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); const struct snd_kcontrol_new *kc; struct soc_enum *se; int mux_index = hdmi->num_cvt + hdmi->num_ports; @@ -1046,8 +1035,8 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *route; - struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct snd_soc_dai_driver *dai_drv = hdmi->dai_drv; char widget_name[NAME_SIZE]; struct hdac_hdmi_cvt *cvt; @@ -1099,7 +1088,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) for (j = 0; j < pin->num_ports; j++) { sprintf(widget_name, "Pin%d-Port%d Mux", pin->nid, pin->ports[j].id); - ret = hdac_hdmi_create_pin_port_muxs(edev, + ret = hdac_hdmi_create_pin_port_muxs(hdev, &pin->ports[j], &widgets[i], widget_name); if (ret < 0) @@ -1134,7 +1123,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) } } - hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i); + hdac_hdmi_add_pinmux_cvt_route(hdev, widgets, route, i); snd_soc_dapm_new_controls(dapm, widgets, ((2 * hdmi->num_ports) + hdmi->num_cvt)); @@ -1146,9 +1135,9 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) } -static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) +static int hdac_hdmi_init_dai_map(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_dai_port_map *dai_map; struct hdac_hdmi_cvt *cvt; int dai_id = 0; @@ -1164,7 +1153,7 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) dai_id++; if (dai_id == HDA_MAX_CVTS) { - dev_warn(&edev->hdev.dev, + dev_warn(&hdev->dev, "Max dais supported: %d\n", dai_id); break; } @@ -1173,9 +1162,9 @@ static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) return 0; } -static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) +static int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_cvt *cvt; char name[NAME_SIZE]; @@ -1190,10 +1179,10 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) list_add_tail(&cvt->head, &hdmi->cvt_list); hdmi->num_cvt++; - return hdac_hdmi_query_cvt_params(&edev->hdev, cvt); + return hdac_hdmi_query_cvt_params(hdev, cvt); } -static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, +static int hdac_hdmi_parse_eld(struct hdac_device *hdev, struct hdac_hdmi_port *port) { unsigned int ver, mnl; @@ -1202,7 +1191,7 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, >> DRM_ELD_VER_SHIFT; if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) { - dev_err(&edev->hdev.dev, "HDMI: Unknown ELD version %d\n", ver); + dev_err(&hdev->dev, "HDMI: Unknown ELD version %d\n", ver); return -EINVAL; } @@ -1210,7 +1199,7 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; if (mnl > ELD_MAX_MNL) { - dev_err(&edev->hdev.dev, "HDMI: MNL Invalid %d\n", mnl); + dev_err(&hdev->dev, "HDMI: MNL Invalid %d\n", mnl); return -EINVAL; } @@ -1222,8 +1211,8 @@ static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, struct hdac_hdmi_port *port) { - struct hdac_ext_device *edev = pin->edev; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = pin->hdev; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; int size = 0; int port_id = -1; @@ -1241,14 +1230,14 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, if (pin->mst_capable) port_id = port->id; - size = snd_hdac_acomp_get_eld(&edev->hdev, pin->nid, port_id, + size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, &port->eld.monitor_present, port->eld.eld_buffer, ELD_MAX_SIZE); if (size > 0) { size = min(size, ELD_MAX_SIZE); - if (hdac_hdmi_parse_eld(edev, port) < 0) + if (hdac_hdmi_parse_eld(hdev, port) < 0) size = -EINVAL; } @@ -1260,11 +1249,11 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, port->eld.eld_size = 0; } - pcm = hdac_hdmi_get_pcm(edev, port); + pcm = hdac_hdmi_get_pcm(hdev, port); if (!port->eld.monitor_present || !port->eld.eld_valid) { - dev_err(&edev->hdev.dev, "%s: disconnect for pin:port %d:%d\n", + dev_err(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", __func__, pin->nid, port->id); /* @@ -1316,9 +1305,9 @@ static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, return 0; } -static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) +static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin; int ret; @@ -1328,7 +1317,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) pin->nid = nid; pin->mst_capable = false; - pin->edev = edev; + pin->hdev = hdev; ret = hdac_hdmi_add_ports(hdmi, pin); if (ret < 0) return ret; @@ -1459,15 +1448,14 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev, * Parse all nodes and store the cvt/pin nids in array * Add one time initialization for pin and cvt widgets */ -static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, +static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, struct snd_soc_dai_driver **dais, int *num_dais) { hda_nid_t nid; int i, num_nodes; struct hdac_hdmi_cvt *temp_cvt, *cvt_next; struct hdac_hdmi_pin *temp_pin, *pin_next; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); - struct hdac_device *hdev = &edev->hdev; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); int ret; hdac_hdmi_skl_enable_all_pins(hdev); @@ -1492,13 +1480,13 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, switch (type) { case AC_WID_AUD_OUT: - ret = hdac_hdmi_add_cvt(edev, nid); + ret = hdac_hdmi_add_cvt(hdev, nid); if (ret < 0) goto free_widgets; break; case AC_WID_PIN: - ret = hdac_hdmi_add_pin(edev, nid); + ret = hdac_hdmi_add_pin(hdev, nid); if (ret < 0) goto free_widgets; break; @@ -1518,7 +1506,7 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, } *num_dais = hdmi->num_cvt; - ret = hdac_hdmi_init_dai_map(edev); + ret = hdac_hdmi_init_dai_map(hdev); if (ret < 0) goto free_widgets; @@ -1544,17 +1532,17 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) { - struct hdac_ext_device *edev = aptr; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_device *hdev = aptr; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin = NULL; struct hdac_hdmi_port *hport = NULL; - struct snd_soc_component *component = edev->scodec; + struct snd_soc_component *component = hdmi->component; int i; /* Don't know how this mapping is derived */ hda_nid_t pin_nid = port + 0x04; - dev_dbg(&edev->hdev.dev, "%s: for pin:%d port=%d\n", __func__, + dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__, pin_nid, pipe); /* @@ -1567,7 +1555,7 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) SNDRV_CTL_POWER_D0) return; - if (atomic_read(&edev->hdev.in_pm)) + if (atomic_read(&hdev->in_pm)) return; list_for_each_entry(pin, &hdmi->pin_list, head) { @@ -1614,15 +1602,15 @@ static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, /* create jack pin kcontrols */ static int create_fill_jack_kcontrols(struct snd_soc_card *card, - struct hdac_ext_device *edev) + struct hdac_device *hdev) { struct hdac_hdmi_pin *pin; struct snd_kcontrol_new *kc; char kc_name[NAME_SIZE], xname[NAME_SIZE]; char *name; int i = 0, j; - struct snd_soc_component *component = edev->scodec; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct snd_soc_component *component = hdmi->component; kc = devm_kcalloc(component->dev, hdmi->num_ports, sizeof(*kc), GFP_KERNEL); @@ -1659,8 +1647,8 @@ static int create_fill_jack_kcontrols(struct snd_soc_card *card, int hdac_hdmi_jack_port_init(struct snd_soc_component *component, struct snd_soc_dapm_context *dapm) { - struct hdac_ext_device *edev = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_pin *pin; struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *route; @@ -1715,7 +1703,7 @@ int hdac_hdmi_jack_port_init(struct snd_soc_component *component, return ret; /* Add Jack Pin switch Kcontrol */ - ret = create_fill_jack_kcontrols(dapm->card, edev); + ret = create_fill_jack_kcontrols(dapm->card, hdev); if (ret < 0) return ret; @@ -1735,8 +1723,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, struct snd_soc_jack *jack) { struct snd_soc_component *component = dai->component; - struct hdac_ext_device *edev = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; struct hdac_hdmi_pcm *pcm; struct snd_pcm *snd_pcm; int err; @@ -1758,7 +1746,7 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, if (snd_pcm) { err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); if (err < 0) { - dev_err(&edev->hdev.dev, + dev_err(&hdev->dev, "chmap control add failed with err: %d for pcm: %d\n", err, device); kfree(pcm); @@ -1772,7 +1760,7 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, } EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); -static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, +static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) { int i; @@ -1781,7 +1769,7 @@ static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, list_for_each_entry(pin, &hdmi->pin_list, head) { if (detect_pin_caps) { - if (hdac_hdmi_get_port_len(edev, pin->nid) == 0) + if (hdac_hdmi_get_port_len(hdev, pin->nid) == 0) pin->mst_capable = false; else pin->mst_capable = true; @@ -1798,68 +1786,68 @@ static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, static int hdmi_codec_probe(struct snd_soc_component *component) { - struct hdac_ext_device *edev = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct hdac_ext_link *hlink = NULL; int ret; - edev->scodec = component; + hdmi->component = component; /* * hold the ref while we probe, also no need to drop the ref on * exit, we call pm_runtime_suspend() so that will do for us */ - hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdev.dev)); + hlink = snd_hdac_ext_bus_get_link(hbus_to_ebus(hdev->bus), + dev_name(&hdev->dev)); if (!hlink) { - dev_err(&edev->hdev.dev, "hdac link not found\n"); + dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(edev->ebus, hlink); + snd_hdac_ext_bus_link_get(hbus_to_ebus(hdev->bus), hlink); ret = create_fill_widget_route_map(dapm); if (ret < 0) return ret; - aops.audio_ptr = edev; + aops.audio_ptr = hdev; ret = snd_hdac_i915_register_notifier(&aops); if (ret < 0) { - dev_err(&edev->hdev.dev, "notifier register failed: err: %d\n", - ret); + dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret); return ret; } - hdac_hdmi_present_sense_all_pins(edev, hdmi, true); + hdac_hdmi_present_sense_all_pins(hdev, hdmi, true); /* Imp: Store the card pointer in hda_codec */ - edev->card = dapm->card->snd_card; + hdmi->card = dapm->card->snd_card; /* * hdac_device core already sets the state to active and calls * get_noresume. So enable runtime and set the device to suspend. */ - pm_runtime_enable(&edev->hdev.dev); - pm_runtime_put(&edev->hdev.dev); - pm_runtime_suspend(&edev->hdev.dev); + pm_runtime_enable(&hdev->dev); + pm_runtime_put(&hdev->dev); + pm_runtime_suspend(&hdev->dev); return 0; } static void hdmi_codec_remove(struct snd_soc_component *component) { - struct hdac_ext_device *edev = snd_soc_component_get_drvdata(component); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; - pm_runtime_disable(&edev->hdev.dev); + pm_runtime_disable(&hdev->dev); } #ifdef CONFIG_PM static int hdmi_codec_prepare(struct device *dev) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdev = &edev->hdev; + struct hdac_device *hdev = dev_to_hdac_dev(dev); - pm_runtime_get_sync(&edev->hdev.dev); + pm_runtime_get_sync(&hdev->dev); /* * Power down afg. @@ -1876,16 +1864,15 @@ static int hdmi_codec_prepare(struct device *dev) static void hdmi_codec_complete(struct device *dev) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); - struct hdac_device *hdev = &edev->hdev; + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); /* Power up afg */ snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - hdac_hdmi_skl_enable_all_pins(&edev->hdev); - hdac_hdmi_skl_enable_dp12(&edev->hdev); + hdac_hdmi_skl_enable_all_pins(hdev); + hdac_hdmi_skl_enable_dp12(hdev); /* * As the ELD notify callback request is not entertained while the @@ -1893,9 +1880,9 @@ static void hdmi_codec_complete(struct device *dev) * all pins here. pin capablity change is not support, so use the * already set pin caps. */ - hdac_hdmi_present_sense_all_pins(edev, hdmi, false); + hdac_hdmi_present_sense_all_pins(hdev, hdmi, false); - pm_runtime_put_sync(&edev->hdev.dev); + pm_runtime_put_sync(&hdev->dev); } #else #define hdmi_codec_prepare NULL @@ -1922,7 +1909,6 @@ static void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx, static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, unsigned char *chmap, int prepared) { - struct hdac_ext_device *edev = to_ehdac_device(hdev); struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); struct hdac_hdmi_port *port; @@ -1938,7 +1924,7 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); list_for_each_entry(port, &pcm->port_list, head) if (prepared) - hdac_hdmi_setup_audio_infoframe(edev, pcm, port); + hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); mutex_unlock(&pcm->lock); } @@ -1987,10 +1973,9 @@ static struct hdac_hdmi_drv_data intel_drv_data = { .vendor_nid = INTEL_VENDOR_NID, }; -static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) +static int hdac_hdmi_dev_probe(struct hdac_device *hdev) { - struct hdac_device *hdev = &edev->hdev; - struct hdac_hdmi_priv *hdmi_priv; + struct hdac_hdmi_priv *hdmi_priv = NULL; struct snd_soc_dai_driver *hdmi_dais = NULL; struct hdac_ext_link *hlink = NULL; int num_dais = 0; @@ -1999,24 +1984,25 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv); /* hold the ref while we probe */ - hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdev.dev)); + hlink = snd_hdac_ext_bus_get_link(hbus_to_ebus(hdev->bus), + dev_name(&hdev->dev)); if (!hlink) { - dev_err(&edev->hdev.dev, "hdac link not found\n"); + dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(edev->ebus, hlink); + snd_hdac_ext_bus_link_get(hbus_to_ebus(hdev->bus), hlink); hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); if (hdmi_priv == NULL) return -ENOMEM; - edev->private_data = hdmi_priv; snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap; hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached; hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; + hdmi_priv->hdev = hdev; if (!hdac_id) return -ENODEV; @@ -2027,7 +2013,7 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) else hdmi_priv->drv_data = &intel_drv_data; - dev_set_drvdata(&hdev->dev, edev); + dev_set_drvdata(&hdev->dev, hdmi_priv); INIT_LIST_HEAD(&hdmi_priv->pin_list); INIT_LIST_HEAD(&hdmi_priv->cvt_list); @@ -2038,15 +2024,15 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) * Turned off in the runtime_suspend during the first explicit * pm_runtime_suspend call. */ - ret = snd_hdac_display_power(edev->hdev.bus, true); + ret = snd_hdac_display_power(hdev->bus, true); if (ret < 0) { - dev_err(&edev->hdev.dev, + dev_err(&hdev->dev, "Cannot turn on display power on i915 err: %d\n", ret); return ret; } - ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais); + ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais); if (ret < 0) { dev_err(&hdev->dev, "Failed in parse and map nid with err: %d\n", ret); @@ -2058,14 +2044,14 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec, hdmi_dais, num_dais); - snd_hdac_ext_bus_link_put(edev->ebus, hlink); + snd_hdac_ext_bus_link_put(hbus_to_ebus(hdev->bus), hlink); return ret; } -static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) +static int hdac_hdmi_dev_remove(struct hdac_device *hdev) { - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(&edev->hdev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pin *pin, *pin_next; struct hdac_hdmi_cvt *cvt, *cvt_next; struct hdac_hdmi_pcm *pcm, *pcm_next; @@ -2105,8 +2091,7 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) #ifdef CONFIG_PM static int hdac_hdmi_runtime_suspend(struct device *dev) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdev = &edev->hdev; + struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; @@ -2129,7 +2114,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) AC_PWRST_D3); err = snd_hdac_display_power(bus, false); if (err < 0) { - dev_err(bus->dev, "Cannot turn on display power on i915\n"); + dev_err(dev, "Cannot turn on display power on i915\n"); return err; } @@ -2146,8 +2131,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) static int hdac_hdmi_runtime_resume(struct device *dev) { - struct hdac_ext_device *edev = to_hda_ext_device(dev); - struct hdac_device *hdev = &edev->hdev; + struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; @@ -2169,12 +2153,12 @@ static int hdac_hdmi_runtime_resume(struct device *dev) err = snd_hdac_display_power(bus, true); if (err < 0) { - dev_err(bus->dev, "Cannot turn on display power on i915\n"); + dev_err(dev, "Cannot turn on display power on i915\n"); return err; } - hdac_hdmi_skl_enable_all_pins(&edev->hdev); - hdac_hdmi_skl_enable_dp12(&edev->hdev); + hdac_hdmi_skl_enable_all_pins(hdev); + hdac_hdmi_skl_enable_dp12(hdev); /* Power up afg */ snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, From de6459706f26907ca949a048bce95bb8076f5ed7 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:34 +0800 Subject: [PATCH 268/285] ALSA: hdac: Remove usage of struct hdac_ext_bus and use hdac_bus instead This patch removes the hdac_ext_bus structure. The legacy and enhanced HDaudio capabilities can be handled in a backward-compatible way without separate definitions. Follow-up patches in this series handle the driver definition. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- include/sound/hdaudio.h | 15 ++ include/sound/hdaudio_ext.h | 74 ++++------ sound/hda/ext/hdac_ext_bus.c | 27 ++-- sound/hda/ext/hdac_ext_controller.c | 55 ++++---- sound/hda/ext/hdac_ext_stream.c | 104 ++++++-------- sound/soc/codecs/hdac_hdmi.c | 22 ++- sound/soc/intel/skylake/skl-messages.c | 50 +++---- sound/soc/intel/skylake/skl-nhlt.c | 8 +- sound/soc/intel/skylake/skl-pcm.c | 112 ++++++++------- sound/soc/intel/skylake/skl-topology.c | 20 ++- sound/soc/intel/skylake/skl-topology.h | 6 +- sound/soc/intel/skylake/skl.c | 181 +++++++++++-------------- sound/soc/intel/skylake/skl.h | 7 +- 13 files changed, 307 insertions(+), 374 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 06536e01ed94b1..17ab1118cad57c 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -250,6 +250,11 @@ struct hdac_rb { * @mlcap: MultiLink capabilities pointer * @gtscap: gts capabilities pointer * @drsmcap: dma resume capabilities pointer + * @num_streams: streams supported + * @idx: HDA link index + * @hlink_list: link list of HDA links + * @lock: lock for link mgmt + * @cmd_dma_state: state of cmd DMAs: CORB and RIRB */ struct hdac_bus { struct device *dev; @@ -317,6 +322,16 @@ struct hdac_bus { /* i915 component interface */ struct i915_audio_component *audio_component; int i915_power_refcount; + + /* parameters required for enhanced capabilities */ + int num_streams; + int idx; + + struct list_head hlink_list; + + struct mutex lock; + bool cmd_dma_state; + }; int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index c1a5ad0e6e3983..e5b0cd1ade19c2 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -4,38 +4,14 @@ #include -/** - * hdac_ext_bus: HDAC extended bus for extended HDA caps - * - * @bus: hdac bus - * @num_streams: streams supported - * @hlink_list: link list of HDA links - * @lock: lock for link mgmt - * @cmd_dma_state: state of cmd DMAs: CORB and RIRB - */ -struct hdac_ext_bus { - struct hdac_bus bus; - int num_streams; - int idx; - - struct list_head hlink_list; - - struct mutex lock; - bool cmd_dma_state; -}; - -int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev, +int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_bus_ops *ops, const struct hdac_io_ops *io_ops); -void snd_hdac_ext_bus_exit(struct hdac_ext_bus *sbus); -int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *sbus, int addr); +void snd_hdac_ext_bus_exit(struct hdac_bus *bus); +int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr); void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev); -void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus); - -#define ebus_to_hbus(ebus) (&(ebus)->bus) -#define hbus_to_ebus(_bus) \ - container_of(_bus, struct hdac_ext_bus, bus) +void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus); #define HDA_CODEC_REV_EXT_ENTRY(_vid, _rev, _name, drv_data) \ { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \ @@ -44,14 +20,14 @@ void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus); #define HDA_CODEC_EXT_ENTRY(_vid, _revid, _name, _drv_data) \ HDA_CODEC_REV_EXT_ENTRY(_vid, _revid, _name, _drv_data) -void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *chip, bool enable); -void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *chip, bool enable); +void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *chip, bool enable); +void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *chip, bool enable); -void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *chip, +void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *chip, bool enable, int index); -int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *bus); -struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *bus, +int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus); +struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, const char *codec_name); enum hdac_ext_stream_type { @@ -100,28 +76,28 @@ struct hdac_ext_stream { #define stream_to_hdac_ext_stream(s) \ container_of(s, struct hdac_ext_stream, hstream) -void snd_hdac_ext_stream_init(struct hdac_ext_bus *bus, +void snd_hdac_ext_stream_init(struct hdac_bus *bus, struct hdac_ext_stream *stream, int idx, int direction, int tag); -int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx, +int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, int num_stream, int dir); -void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus); -void snd_hdac_link_free_all(struct hdac_ext_bus *ebus); -struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *bus, +void snd_hdac_stream_free_all(struct hdac_bus *bus); +void snd_hdac_link_free_all(struct hdac_bus *bus); +struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type); -void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *bus, +void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); -void snd_hdac_ext_stop_streams(struct hdac_ext_bus *sbus); +void snd_hdac_ext_stop_streams(struct hdac_bus *bus); -int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, struct hdac_ext_stream *stream, u32 value); -int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, struct hdac_ext_stream *stream); -void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, bool enable, int index); -int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, struct hdac_ext_stream *stream, u32 value); int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value); @@ -144,17 +120,15 @@ struct hdac_ext_link { int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link); int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link); -int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus); -int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus); +int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus); +int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus); void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, int stream); void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, int stream); -int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus, - struct hdac_ext_link *link); -int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, - struct hdac_ext_link *link); +int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, struct hdac_ext_link *link); +int snd_hdac_ext_bus_link_put(struct hdac_bus *bus, struct hdac_ext_link *link); /* update register macro */ #define snd_hdac_updatel(addr, reg, mask, val) \ diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 0e4823fdd41197..77547ede9ae84c 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -87,7 +87,7 @@ static const struct hdac_io_ops hdac_ext_default_io = { * * Returns 0 if successful, or a negative error code. */ -int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev, +int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_bus_ops *ops, const struct hdac_io_ops *io_ops) { @@ -98,15 +98,15 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev, if (io_ops == NULL) io_ops = &hdac_ext_default_io; - ret = snd_hdac_bus_init(&ebus->bus, dev, ops, io_ops); + ret = snd_hdac_bus_init(bus, dev, ops, io_ops); if (ret < 0) return ret; - INIT_LIST_HEAD(&ebus->hlink_list); - ebus->idx = idx++; + INIT_LIST_HEAD(&bus->hlink_list); + bus->idx = idx++; - mutex_init(&ebus->lock); - ebus->cmd_dma_state = true; + mutex_init(&bus->lock); + bus->cmd_dma_state = true; return 0; } @@ -116,10 +116,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus * @ebus: the pointer to extended bus object */ -void snd_hdac_ext_bus_exit(struct hdac_ext_bus *ebus) +void snd_hdac_ext_bus_exit(struct hdac_bus *bus) { - snd_hdac_bus_exit(&ebus->bus); - WARN_ON(!list_empty(&ebus->hlink_list)); + snd_hdac_bus_exit(bus); + WARN_ON(!list_empty(&bus->hlink_list)); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit); @@ -135,10 +135,9 @@ static void default_release(struct device *dev) * * Returns zero for success or a negative error code. */ -int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) +int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr) { struct hdac_device *hdev = NULL; - struct hdac_bus *bus = ebus_to_hbus(ebus); char name[15]; int ret; @@ -148,7 +147,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) hdev->bus = bus; - snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); + snprintf(name, sizeof(name), "ehdaudio%dD%d", bus->idx, addr); ret = snd_hdac_device_init(hdev, bus, name, addr); if (ret < 0) { @@ -185,14 +184,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); * * @ebus: HD-audio extended bus */ -void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus) +void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus) { struct hdac_device *codec, *__codec; /* * we need to remove all the codec devices objects created in the * snd_hdac_ext_bus_device_init */ - list_for_each_entry_safe(codec, __codec, &ebus->bus.codec_list, list) { + list_for_each_entry_safe(codec, __codec, &bus->codec_list, list) { snd_hdac_device_unregister(codec); put_device(&codec->dev); } diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index f83d5e3c426204..5bc4a1d587d4f1 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -39,9 +39,8 @@ * @ebus: HD-audio extended core bus * @enable: flag to turn on/off the capability */ -void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable) +void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable) { - struct hdac_bus *bus = &ebus->bus; if (!bus->ppcap) { dev_err(bus->dev, "Address of PP capability is NULL"); @@ -60,9 +59,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable); * @ebus: HD-audio extended core bus * @enable: flag to enable/disable interrupt */ -void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable) +void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable) { - struct hdac_bus *bus = &ebus->bus; if (!bus->ppcap) { dev_err(bus->dev, "Address of PP capability is NULL\n"); @@ -89,12 +87,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable); * in hlink_list of extended hdac bus * Note: this will be freed on bus exit by driver */ -int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) +int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus) { int idx; u32 link_count; struct hdac_ext_link *hlink; - struct hdac_bus *bus = &ebus->bus; link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; @@ -114,7 +111,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) /* since link in On, update the ref */ hlink->ref_count = 1; - list_add_tail(&hlink->list, &ebus->hlink_list); + list_add_tail(&hlink->list, &bus->hlink_list); } return 0; @@ -127,12 +124,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities); * @ebus: HD-audio ext core bus */ -void snd_hdac_link_free_all(struct hdac_ext_bus *ebus) +void snd_hdac_link_free_all(struct hdac_bus *bus) { struct hdac_ext_link *l; - while (!list_empty(&ebus->hlink_list)) { - l = list_first_entry(&ebus->hlink_list, struct hdac_ext_link, list); + while (!list_empty(&bus->hlink_list)) { + l = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list); list_del(&l->list); kfree(l); } @@ -144,7 +141,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_link_free_all); * @ebus: HD-audio extended core bus * @codec_name: codec name */ -struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus, +struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, const char *codec_name) { int i; @@ -153,10 +150,10 @@ struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus, if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2) return NULL; - if (ebus->idx != bus_idx) + if (bus->idx != bus_idx) return NULL; - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { for (i = 0; i < HDA_MAX_CODECS; i++) { if (hlink->lsdiid & (0x1 << addr)) return hlink; @@ -219,12 +216,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down); * snd_hdac_ext_bus_link_power_up_all -power up all hda link * @ebus: HD-audio extended bus */ -int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus) +int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink = NULL; int ret; - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA); ret = check_hdac_link_power_active(hlink, true); @@ -240,12 +237,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all); * snd_hdac_ext_bus_link_power_down_all -power down all hda link * @ebus: HD-audio extended bus */ -int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus) +int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink = NULL; int ret; - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0); ret = check_hdac_link_power_active(hlink, false); if (ret < 0) @@ -256,21 +253,21 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus) } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all); -int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus, +int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, struct hdac_ext_link *link) { int ret = 0; - mutex_lock(&ebus->lock); + mutex_lock(&bus->lock); /* * if we move from 0 to 1, count will be 1 so power up this link * as well, also check the dma status and trigger that */ if (++link->ref_count == 1) { - if (!ebus->cmd_dma_state) { - snd_hdac_bus_init_cmd_io(&ebus->bus); - ebus->cmd_dma_state = true; + if (!bus->cmd_dma_state) { + snd_hdac_bus_init_cmd_io(bus); + bus->cmd_dma_state = true; } ret = snd_hdac_ext_bus_link_power_up(link); @@ -285,19 +282,19 @@ int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus, snd_hdac_chip_writew(bus, STATESTS, bus->codec_mask); } - mutex_unlock(&ebus->lock); + mutex_unlock(&bus->lock); return ret; } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get); -int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, +int snd_hdac_ext_bus_link_put(struct hdac_bus *bus, struct hdac_ext_link *link) { int ret = 0; struct hdac_ext_link *hlink; bool link_up = false; - mutex_lock(&ebus->lock); + mutex_lock(&bus->lock); /* * if we move from 1 to 0, count will be 0 @@ -310,7 +307,7 @@ int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, * now check if all links are off, if so turn off * cmd dma as well */ - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { if (hlink->ref_count) { link_up = true; break; @@ -318,12 +315,12 @@ int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, } if (!link_up) { - snd_hdac_bus_stop_cmd_io(&ebus->bus); - ebus->cmd_dma_state = false; + snd_hdac_bus_stop_cmd_io(bus); + bus->cmd_dma_state = false; } } - mutex_unlock(&ebus->lock); + mutex_unlock(&bus->lock); return ret; } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index c96d7a7a36af03..1bd27576db98d5 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -25,7 +25,7 @@ /** * snd_hdac_ext_stream_init - initialize each stream (aka device) - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: HD-audio ext core stream object to initialize * @idx: stream index number * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) @@ -34,18 +34,16 @@ * initialize the stream, if ppcap is enabled then init those and then * invoke hdac stream initialization routine */ -void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_init(struct hdac_bus *bus, struct hdac_ext_stream *stream, int idx, int direction, int tag) { - struct hdac_bus *bus = &ebus->bus; - if (bus->ppcap) { stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + AZX_PPHC_INTERVAL * idx; stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + - AZX_PPLC_MULTI * ebus->num_streams + + AZX_PPLC_MULTI * bus->num_streams + AZX_PPLC_INTERVAL * idx; } @@ -71,12 +69,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); /** * snd_hdac_ext_stream_init_all - create and initialize the stream objects * for an extended hda bus - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @start_idx: start index for streams * @num_stream: number of streams to initialize * @dir: direction of streams */ -int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx, +int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, int num_stream, int dir) { int stream_tag = 0; @@ -88,7 +86,7 @@ int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx, if (!stream) return -ENOMEM; tag = ++stream_tag; - snd_hdac_ext_stream_init(ebus, stream, idx, dir, tag); + snd_hdac_ext_stream_init(bus, stream, idx, dir, tag); idx++; } @@ -100,17 +98,16 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); /** * snd_hdac_stream_free_all - free hdac extended stream objects * - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus */ -void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus) +void snd_hdac_stream_free_all(struct hdac_bus *bus) { struct hdac_stream *s, *_s; struct hdac_ext_stream *stream; - struct hdac_bus *bus = ebus_to_hbus(ebus); list_for_each_entry_safe(s, _s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); list_del(&s->list); kfree(stream); } @@ -119,15 +116,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); /** * snd_hdac_ext_stream_decouple - decouple the hdac stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: HD-audio ext core stream object to initialize * @decouple: flag to decouple */ -void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *stream, bool decouple) { struct hdac_stream *hstream = &stream->hstream; - struct hdac_bus *bus = &ebus->bus; u32 val; int mask = AZX_PPCTL_PROCEN(hstream->index); @@ -251,19 +247,18 @@ void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id); static struct hdac_ext_stream * -hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus, +hdac_ext_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; struct hdac_stream *stream = NULL; - struct hdac_bus *hbus = &ebus->bus; - if (!hbus->ppcap) { - dev_err(hbus->dev, "stream type not supported\n"); + if (!bus->ppcap) { + dev_err(bus->dev, "stream type not supported\n"); return NULL; } - list_for_each_entry(stream, &hbus->stream_list, list) { + list_for_each_entry(stream, &bus->stream_list, list) { struct hdac_ext_stream *hstream = container_of(stream, struct hdac_ext_stream, hstream); @@ -277,34 +272,33 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus, } if (!hstream->link_locked) { - snd_hdac_ext_stream_decouple(ebus, hstream, true); + snd_hdac_ext_stream_decouple(bus, hstream, true); res = hstream; break; } } if (res) { - spin_lock_irq(&hbus->reg_lock); + spin_lock_irq(&bus->reg_lock); res->link_locked = 1; res->link_substream = substream; - spin_unlock_irq(&hbus->reg_lock); + spin_unlock_irq(&bus->reg_lock); } return res; } static struct hdac_ext_stream * -hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, +hdac_ext_host_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; struct hdac_stream *stream = NULL; - struct hdac_bus *hbus = &ebus->bus; - if (!hbus->ppcap) { - dev_err(hbus->dev, "stream type not supported\n"); + if (!bus->ppcap) { + dev_err(bus->dev, "stream type not supported\n"); return NULL; } - list_for_each_entry(stream, &hbus->stream_list, list) { + list_for_each_entry(stream, &bus->stream_list, list) { struct hdac_ext_stream *hstream = container_of(stream, struct hdac_ext_stream, hstream); @@ -313,17 +307,17 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, if (!stream->opened) { if (!hstream->decoupled) - snd_hdac_ext_stream_decouple(ebus, hstream, true); + snd_hdac_ext_stream_decouple(bus, hstream, true); res = hstream; break; } } if (res) { - spin_lock_irq(&hbus->reg_lock); + spin_lock_irq(&bus->reg_lock); res->hstream.opened = 1; res->hstream.running = 0; res->hstream.substream = substream; - spin_unlock_irq(&hbus->reg_lock); + spin_unlock_irq(&bus->reg_lock); } return res; @@ -331,7 +325,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, /** * snd_hdac_ext_stream_assign - assign a stream for the PCM - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @substream: PCM substream to assign * @type: type of stream (coupled, host or link stream) * @@ -346,27 +340,26 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, * the same stream object when it's used beforehand. when a stream is * decoupled, it becomes a host stream and link stream. */ -struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *ebus, +struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type) { struct hdac_ext_stream *hstream = NULL; struct hdac_stream *stream = NULL; - struct hdac_bus *hbus = &ebus->bus; switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: - stream = snd_hdac_stream_assign(hbus, substream); + stream = snd_hdac_stream_assign(bus, substream); if (stream) hstream = container_of(stream, struct hdac_ext_stream, hstream); return hstream; case HDAC_EXT_STREAM_TYPE_HOST: - return hdac_ext_host_stream_assign(ebus, substream); + return hdac_ext_host_stream_assign(bus, substream); case HDAC_EXT_STREAM_TYPE_LINK: - return hdac_ext_link_stream_assign(ebus, substream); + return hdac_ext_link_stream_assign(bus, substream); default: return NULL; @@ -384,7 +377,6 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) { struct hdac_bus *bus = stream->hstream.bus; - struct hdac_ext_bus *ebus = hbus_to_ebus(bus); switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: @@ -393,13 +385,13 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) case HDAC_EXT_STREAM_TYPE_HOST: if (stream->decoupled && !stream->link_locked) - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); snd_hdac_stream_release(&stream->hstream); break; case HDAC_EXT_STREAM_TYPE_LINK: if (stream->decoupled && !stream->hstream.opened) - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); spin_lock_irq(&bus->reg_lock); stream->link_locked = 0; stream->link_substream = NULL; @@ -415,16 +407,15 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); /** * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @enable: flag to enable/disable SPIB * @index: stream index for which SPIB need to be enabled */ -void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus, bool enable, int index) { u32 mask = 0; u32 register_mask = 0; - struct hdac_bus *bus = &ebus->bus; if (!bus->spbcap) { dev_err(bus->dev, "Address of SPB capability is NULL\n"); @@ -446,14 +437,13 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); /** * snd_hdac_ext_stream_set_spib - sets the spib value of a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: hdac_ext_stream * @value: spib value to set */ -int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, struct hdac_ext_stream *stream, u32 value) { - struct hdac_bus *bus = &ebus->bus; if (!bus->spbcap) { dev_err(bus->dev, "Address of SPB capability is NULL\n"); @@ -468,15 +458,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); /** * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: hdac_ext_stream * * Return maxfifo for the stream */ -int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, struct hdac_ext_stream *stream) { - struct hdac_bus *bus = &ebus->bus; if (!bus->spbcap) { dev_err(bus->dev, "Address of SPB capability is NULL\n"); @@ -490,11 +479,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); /** * snd_hdac_ext_stop_streams - stop all stream if running - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus */ -void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus) +void snd_hdac_ext_stop_streams(struct hdac_bus *bus) { - struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_stream *stream; if (bus->chip_init) { @@ -507,16 +495,15 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); /** * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @enable: flag to enable/disable DRSM * @index: stream index for which DRSM need to be enabled */ -void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus, +void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, bool enable, int index) { u32 mask = 0; u32 register_mask = 0; - struct hdac_bus *bus = &ebus->bus; if (!bus->drsmcap) { dev_err(bus->dev, "Address of DRSM capability is NULL\n"); @@ -538,14 +525,13 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); /** * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: hdac_ext_stream * @value: dpib value to set */ -int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus, +int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, struct hdac_ext_stream *stream, u32 value) { - struct hdac_bus *bus = &ebus->bus; if (!bus->drsmcap) { dev_err(bus->dev, "Address of DRSM capability is NULL\n"); @@ -560,7 +546,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); /** * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream - * @ebus: HD-audio ext core bus + * @bus: HD-audio core bus * @stream: hdac_ext_stream * @value: lpib value to set */ diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index f1e235817a6596..c3ccc8d9c91d10 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1799,14 +1799,13 @@ static int hdmi_codec_probe(struct snd_soc_component *component) * hold the ref while we probe, also no need to drop the ref on * exit, we call pm_runtime_suspend() so that will do for us */ - hlink = snd_hdac_ext_bus_get_link(hbus_to_ebus(hdev->bus), - dev_name(&hdev->dev)); + hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(hbus_to_ebus(hdev->bus), hlink); + snd_hdac_ext_bus_link_get(hdev->bus, hlink); ret = create_fill_widget_route_map(dapm); if (ret < 0) @@ -1984,14 +1983,13 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv); /* hold the ref while we probe */ - hlink = snd_hdac_ext_bus_get_link(hbus_to_ebus(hdev->bus), - dev_name(&hdev->dev)); + hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev)); if (!hlink) { dev_err(&hdev->dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(hbus_to_ebus(hdev->bus), hlink); + snd_hdac_ext_bus_link_get(hdev->bus, hlink); hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); if (hdmi_priv == NULL) @@ -2044,7 +2042,7 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec, hdmi_dais, num_dais); - snd_hdac_ext_bus_link_put(hbus_to_ebus(hdev->bus), hlink); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); return ret; } @@ -2093,7 +2091,6 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; - struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; int err; @@ -2118,13 +2115,13 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) return err; } - hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + hlink = snd_hdac_ext_bus_get_link(bus, dev_name(dev)); if (!hlink) { dev_err(dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_put(ebus, hlink); + snd_hdac_ext_bus_link_put(bus, hlink); return 0; } @@ -2133,7 +2130,6 @@ static int hdac_hdmi_runtime_resume(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_bus *bus = hdev->bus; - struct hdac_ext_bus *ebus = hbus_to_ebus(bus); struct hdac_ext_link *hlink = NULL; int err; @@ -2143,13 +2139,13 @@ static int hdac_hdmi_runtime_resume(struct device *dev) if (!bus) return 0; - hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + hlink = snd_hdac_ext_bus_get_link(bus, dev_name(dev)); if (!hlink) { dev_err(dev, "hdac link not found\n"); return -EIO; } - snd_hdac_ext_bus_link_get(ebus, hlink); + snd_hdac_ext_bus_link_get(bus, hlink); err = snd_hdac_display_power(bus, true); if (err < 0) { diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 57d4a58522a695..6e44779db3e3cc 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -33,8 +33,7 @@ static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); if (!bus) return -ENODEV; @@ -44,8 +43,7 @@ static int skl_alloc_dma_buf(struct device *dev, static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); if (!bus) return -ENODEV; @@ -89,8 +87,7 @@ void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) static int skl_dsp_setup_spib(struct device *dev, unsigned int size, int stream_tag, int enable) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream = snd_hdac_get_stream(bus, SNDRV_PCM_STREAM_PLAYBACK, stream_tag); struct hdac_ext_stream *estream; @@ -100,10 +97,10 @@ static int skl_dsp_setup_spib(struct device *dev, unsigned int size, estream = stream_to_hdac_ext_stream(stream); /* enable/disable SPIB for this hdac stream */ - snd_hdac_ext_stream_spbcap_enable(ebus, enable, stream->index); + snd_hdac_ext_stream_spbcap_enable(bus, enable, stream->index); /* set the spib value */ - snd_hdac_ext_stream_set_spib(ebus, estream, size); + snd_hdac_ext_stream_set_spib(bus, estream, size); return 0; } @@ -111,8 +108,7 @@ static int skl_dsp_setup_spib(struct device *dev, unsigned int size, static int skl_dsp_prepare(struct device *dev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_ext_stream *estream; struct hdac_stream *stream; struct snd_pcm_substream substream; @@ -124,7 +120,7 @@ static int skl_dsp_prepare(struct device *dev, unsigned int format, memset(&substream, 0, sizeof(substream)); substream.stream = SNDRV_PCM_STREAM_PLAYBACK; - estream = snd_hdac_ext_stream_assign(ebus, &substream, + estream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST); if (!estream) return -ENODEV; @@ -143,9 +139,8 @@ static int skl_dsp_prepare(struct device *dev, unsigned int format, static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream; - struct hdac_bus *bus = ebus_to_hbus(ebus); if (!bus) return -ENODEV; @@ -163,10 +158,9 @@ static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) static int skl_dsp_cleanup(struct device *dev, struct snd_dma_buffer *dmab, int stream_tag) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream; struct hdac_ext_stream *estream; - struct hdac_bus *bus = ebus_to_hbus(ebus); if (!bus) return -ENODEV; @@ -270,8 +264,7 @@ const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id) int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; - struct hdac_ext_bus *ebus = &skl->ebus; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct skl_dsp_loader_ops loader_ops; int irq = bus->irq; const struct skl_dsp_ops *ops; @@ -279,8 +272,8 @@ int skl_init_dsp(struct skl *skl) int ret; /* enable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); - snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); + snd_hdac_ext_bus_ppcap_enable(bus, true); + snd_hdac_ext_bus_ppcap_int_enable(bus, true); /* read the BAR of the ADSP MMIO */ mmio_base = pci_ioremap_bar(skl->pci, 4); @@ -335,12 +328,11 @@ int skl_init_dsp(struct skl *skl) int skl_free_dsp(struct skl *skl) { - struct hdac_ext_bus *ebus = &skl->ebus; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct skl_sst *ctx = skl->skl_sst; /* disable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); + snd_hdac_ext_bus_ppcap_int_enable(bus, false); ctx->dsp_ops->cleanup(bus->dev, ctx); @@ -383,10 +375,11 @@ int skl_suspend_late_dsp(struct skl *skl) int skl_suspend_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; + struct hdac_bus *bus = skl_to_bus(skl); int ret; /* if ppcap is not supported return 0 */ - if (!skl->ebus.bus.ppcap) + if (!bus->ppcap) return 0; ret = skl_dsp_sleep(ctx->dsp); @@ -394,8 +387,8 @@ int skl_suspend_dsp(struct skl *skl) return ret; /* disable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); - snd_hdac_ext_bus_ppcap_enable(&skl->ebus, false); + snd_hdac_ext_bus_ppcap_int_enable(bus, false); + snd_hdac_ext_bus_ppcap_enable(bus, false); return 0; } @@ -403,15 +396,16 @@ int skl_suspend_dsp(struct skl *skl) int skl_resume_dsp(struct skl *skl) { struct skl_sst *ctx = skl->skl_sst; + struct hdac_bus *bus = skl_to_bus(skl); int ret; /* if ppcap is not supported return 0 */ - if (!skl->ebus.bus.ppcap) + if (!bus->ppcap) return 0; /* enable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); - snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); + snd_hdac_ext_bus_ppcap_enable(bus, true); + snd_hdac_ext_bus_ppcap_int_enable(bus, true); /* check if DSP 1st boot is done */ if (skl->skl_sst->is_first_boot == true) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index b9b140275be099..01a050cf877537 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -141,7 +141,7 @@ struct nhlt_specific_cfg { struct nhlt_fmt *fmt; struct nhlt_endpoint *epnt; - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct device *dev = bus->dev; struct nhlt_specific_cfg *sp_config; struct nhlt_acpi_table *nhlt = skl->nhlt; @@ -228,7 +228,7 @@ static void skl_nhlt_trim_space(char *trim) int skl_nhlt_update_topology_bin(struct skl *skl) { struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct device *dev = bus->dev; dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n", @@ -248,8 +248,8 @@ static ssize_t skl_nhlt_platform_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; char platform_id[32]; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index c466265a2fbbc5..ca5f0d76197a6f 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -67,16 +67,15 @@ struct hdac_ext_stream *get_hdac_ext_stream(struct snd_pcm_substream *substream) return substream->runtime->private_data; } -static struct hdac_ext_bus *get_bus_ctx(struct snd_pcm_substream *substream) +static struct hdac_bus *get_bus_ctx(struct snd_pcm_substream *substream) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct hdac_stream *hstream = hdac_stream(stream); struct hdac_bus *bus = hstream->bus; - - return hbus_to_ebus(bus); + return bus; } -static int skl_substream_alloc_pages(struct hdac_ext_bus *ebus, +static int skl_substream_alloc_pages(struct hdac_bus *bus, struct snd_pcm_substream *substream, size_t size) { @@ -95,7 +94,7 @@ static int skl_substream_free_pages(struct hdac_bus *bus, return snd_pcm_lib_free_pages(substream); } -static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, +static void skl_set_pcm_constrains(struct hdac_bus *bus, struct snd_pcm_runtime *runtime) { snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -105,9 +104,9 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, 20, 178000000); } -static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus) +static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus) { - if ((ebus_to_hbus(ebus))->ppcap) + if (bus->ppcap) return HDAC_EXT_STREAM_TYPE_HOST; else return HDAC_EXT_STREAM_TYPE_COUPLED; @@ -123,9 +122,9 @@ static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *e static void skl_set_suspend_active(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool enable) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct snd_soc_dapm_widget *w; - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; @@ -140,8 +139,7 @@ static void skl_set_suspend_active(struct snd_pcm_substream *substream, int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); unsigned int format_val; struct hdac_stream *hstream; struct hdac_ext_stream *stream; @@ -153,7 +151,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) return -EINVAL; stream = stream_to_hdac_ext_stream(hstream); - snd_hdac_ext_stream_decouple(ebus, stream, true); + snd_hdac_ext_stream_decouple(bus, stream, true); format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, params->format, params->host_bps, 0); @@ -177,8 +175,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); unsigned int format_val; struct hdac_stream *hstream; struct hdac_ext_stream *stream; @@ -190,7 +187,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) return -EINVAL; stream = stream_to_hdac_ext_stream(hstream); - snd_hdac_ext_stream_decouple(ebus, stream, true); + snd_hdac_ext_stream_decouple(bus, stream, true); format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, params->format, params->link_bps, 0); @@ -201,7 +198,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) snd_hdac_ext_link_stream_setup(stream, format_val); - list_for_each_entry(link, &ebus->hlink_list, list) { + list_for_each_entry(link, &bus->hlink_list, list) { if (link->index == params->link_index) snd_hdac_ext_link_set_stream_id(link, hstream->stream_tag); @@ -215,7 +212,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) static int skl_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream; struct snd_pcm_runtime *runtime = substream->runtime; struct skl_dma_params *dma_params; @@ -224,12 +221,12 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); - stream = snd_hdac_ext_stream_assign(ebus, substream, - skl_get_host_stream_type(ebus)); + stream = snd_hdac_ext_stream_assign(bus, substream, + skl_get_host_stream_type(bus)); if (stream == NULL) return -EBUSY; - skl_set_pcm_constrains(ebus, runtime); + skl_set_pcm_constrains(bus, runtime); /* * disable WALLCLOCK timestamps for capture streams @@ -285,7 +282,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct skl_pipe_params p_params = {0}; @@ -293,7 +290,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, int ret, dma_id; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); - ret = skl_substream_alloc_pages(ebus, substream, + ret = skl_substream_alloc_pages(bus, substream, params_buffer_bytes(params)); if (ret < 0) return ret; @@ -327,14 +324,14 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct skl_dma_params *dma_params = NULL; - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); struct skl_module_cfg *mconfig; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); - snd_hdac_ext_stream_release(stream, skl_get_host_stream_type(ebus)); + snd_hdac_ext_stream_release(stream, skl_get_host_stream_type(bus)); dma_params = snd_soc_dai_get_dma_data(dai, substream); /* @@ -364,7 +361,7 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, static int skl_pcm_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); @@ -372,7 +369,7 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, snd_hdac_stream_cleanup(hdac_stream(stream)); hdac_stream(stream)->prepared = 0; - return skl_substream_free_pages(ebus_to_hbus(ebus), substream); + return skl_substream_free_pages(bus, substream); } static int skl_be_hw_params(struct snd_pcm_substream *substream, @@ -392,8 +389,7 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, static int skl_decoupled_trigger(struct snd_pcm_substream *substream, int cmd) { - struct hdac_ext_bus *ebus = get_bus_ctx(substream); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream; int start; unsigned long cookie; @@ -442,7 +438,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct skl *skl = get_skl_ctx(dai->dev); struct skl_sst *ctx = skl->skl_sst; struct skl_module_cfg *mconfig; - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); struct snd_soc_dapm_widget *w; int ret; @@ -464,9 +460,9 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, * dpib & lpib position to resume before starting the * DMA */ - snd_hdac_ext_stream_drsm_enable(ebus, true, + snd_hdac_ext_stream_drsm_enable(bus, true, hdac_stream(stream)->index); - snd_hdac_ext_stream_set_dpibr(ebus, stream, + snd_hdac_ext_stream_set_dpibr(bus, stream, stream->lpib); snd_hdac_ext_stream_set_lpib(stream, stream->lpib); } @@ -500,14 +496,14 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, ret = skl_decoupled_trigger(substream, cmd); if ((cmd == SNDRV_PCM_TRIGGER_SUSPEND) && !w->ignore_suspend) { /* save the dpib and lpib positions */ - stream->dpib = readl(ebus->bus.remap_addr + + stream->dpib = readl(bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(stream)->index)); stream->lpib = snd_hdac_stream_get_pos_lpib( hdac_stream(stream)); - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); } break; @@ -518,11 +514,12 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } + static int skl_link_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *link_dev; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct snd_soc_dai *codec_dai = rtd->codec_dai; @@ -530,14 +527,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, struct hdac_ext_link *link; int stream_tag; - link_dev = snd_hdac_ext_stream_assign(ebus, substream, + link_dev = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_LINK); if (!link_dev) return -EBUSY; snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); - link = snd_hdac_ext_bus_get_link(ebus, codec_dai->component->name); + link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); if (!link) return -EINVAL; @@ -582,7 +579,7 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream, { struct hdac_ext_stream *link_dev = snd_soc_dai_get_dma_data(dai, substream); - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); @@ -598,7 +595,7 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: snd_hdac_ext_link_stream_clear(link_dev); if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); break; default: @@ -610,7 +607,7 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream, static int skl_link_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct hdac_ext_stream *link_dev = snd_soc_dai_get_dma_data(dai, substream); @@ -620,7 +617,7 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, link_dev->link_prepared = 0; - link = snd_hdac_ext_bus_get_link(ebus, rtd->codec_dai->component->name); + link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); if (!link) return -EINVAL; @@ -1014,8 +1011,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream) static int skl_coupled_trigger(struct snd_pcm_substream *substream, int cmd) { - struct hdac_ext_bus *ebus = get_bus_ctx(substream); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream; struct snd_pcm_substream *s; bool start; @@ -1088,9 +1084,9 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream, static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); - if (!(ebus_to_hbus(ebus))->ppcap) + if (!bus->ppcap) return skl_coupled_trigger(substream, cmd); return 0; @@ -1100,7 +1096,7 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer (struct snd_pcm_substream *substream) { struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream); - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); unsigned int pos; /* @@ -1125,12 +1121,12 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - pos = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE + + pos = readl(bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(hstream)->index)); } else { udelay(20); - readl(ebus->bus.remap_addr + + readl(bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(hstream)->index)); @@ -1215,11 +1211,11 @@ static void skl_pcm_free(struct snd_pcm *pcm) static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = rtd->cpu_dai; - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct snd_pcm *pcm = rtd->pcm; unsigned int size; int retval = 0; - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); if (dai->driver->playback.channels_min || dai->driver->capture.channels_min) { @@ -1329,19 +1325,19 @@ static int skl_populate_modules(struct skl *skl) static int skl_platform_soc_probe(struct snd_soc_component *component) { - struct hdac_ext_bus *ebus = dev_get_drvdata(component->dev); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = dev_get_drvdata(component->dev); + struct skl *skl = bus_to_skl(bus); const struct skl_dsp_ops *ops; int ret; pm_runtime_get_sync(component->dev); - if ((ebus_to_hbus(ebus))->ppcap) { + if (bus->ppcap) { skl->component = component; /* init debugfs */ skl->debugfs = skl_debugfs_init(skl); - ret = skl_tplg_init(component, ebus); + ret = skl_tplg_init(component, bus); if (ret < 0) { dev_err(component->dev, "Failed to init topology!\n"); return ret; @@ -1398,10 +1394,10 @@ static const struct snd_soc_component_driver skl_component = { int skl_platform_register(struct device *dev) { int ret; - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct skl *skl = ebus_to_skl(ebus); struct snd_soc_dai_driver *dais; int num_dais = ARRAY_SIZE(skl_platform_dai); + struct hdac_bus *bus = dev_get_drvdata(dev); + struct skl *skl = bus_to_skl(bus); INIT_LIST_HEAD(&skl->ppl_list); INIT_LIST_HEAD(&skl->bind_list); @@ -1437,8 +1433,8 @@ int skl_platform_register(struct device *dev) int skl_platform_unregister(struct device *dev) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = dev_get_drvdata(dev); + struct skl *skl = bus_to_skl(bus); struct skl_module_deferred_bind *modules, *tmp; if (!list_empty(&skl->bind_list)) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 6ac081f1f215df..1496aac9d23547 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -933,7 +933,7 @@ static int skl_tplg_find_moduleid_from_uuid(struct skl *skl, struct soc_bytes_ext *sb = (void *) k->private_value; struct skl_algo_data *bc = (struct skl_algo_data *)sb->dobj.private; struct skl_kpb_params *uuid_params, *params; - struct hdac_bus *bus = ebus_to_hbus(skl_to_ebus(skl)); + struct hdac_bus *bus = skl_to_bus(skl); int i, size, module_id; if (bc->set_params == SKL_PARAM_BIND && bc->max) { @@ -2856,9 +2856,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_tplg_dapm_widget *tplg_w) { int ret; - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); + struct skl *skl = bus_to_skl(bus); struct skl_module_cfg *mconfig; if (!tplg_w->priv.size) @@ -2965,8 +2964,7 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt, struct soc_bytes_ext *sb; struct snd_soc_tplg_bytes_control *tplg_bc; struct snd_soc_tplg_enum_control *tplg_ec; - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); struct soc_enum *se; switch (hdr->ops.info) { @@ -3450,9 +3448,8 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_tplg_manifest *manifest) { - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); + struct skl *skl = bus_to_skl(bus); /* proceed only if we have private data defined */ if (manifest->priv.size == 0) @@ -3541,12 +3538,11 @@ static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe) /* * SKL topology init routine */ -int skl_tplg_init(struct snd_soc_component *component, struct hdac_ext_bus *ebus) +int skl_tplg_init(struct snd_soc_component *component, struct hdac_bus *bus) { int ret; const struct firmware *fw; - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); struct skl_pipeline *ppl; ret = request_firmware(&fw, skl->tplg_name, bus->dev); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 8fcba4cfca6cfc..a9e8607bd7fa61 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -458,9 +458,9 @@ enum skl_channel { static inline struct skl *get_skl_ctx(struct device *dev) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = dev_get_drvdata(dev); - return ebus_to_skl(ebus); + return bus_to_skl(bus); } int skl_tplg_be_update_params(struct snd_soc_dai *dai, @@ -470,7 +470,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, struct skl_pipe_params *params, int stream); int skl_tplg_init(struct snd_soc_component *component, - struct hdac_ext_bus *ebus); + struct hdac_bus *ebus); struct skl_module_cfg *skl_tplg_fe_get_cpr_module( struct snd_soc_dai *dai, int stream); int skl_tplg_update_pipe_params(struct device *dev, diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 8def7cc58586e7..4be003540202a6 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -53,7 +53,7 @@ static void skl_update_pci_byte(struct pci_dev *pci, unsigned int reg, static void skl_init_pci(struct skl *skl) { - struct hdac_ext_bus *ebus = &skl->ebus; + struct hdac_bus *bus = skl_to_bus(skl); /* * Clear bits 0-2 of PCI register TCSEL (at offset 0x44) @@ -62,7 +62,7 @@ static void skl_init_pci(struct skl *skl) * codecs. * The PCI register TCSEL is defined in the Intel manuals. */ - dev_dbg(ebus_to_hbus(ebus)->dev, "Clearing TCSEL\n"); + dev_dbg(bus->dev, "Clearing TCSEL\n"); skl_update_pci_byte(skl->pci, AZX_PCIREG_TCSEL, 0x07, 0); } @@ -102,8 +102,7 @@ static void skl_enable_miscbdcge(struct device *dev, bool enable) static void skl_clock_power_gating(struct device *dev, bool enable) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); u32 val; /* Update PDCGE bit of CGCTL register */ @@ -138,8 +137,7 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) void skl_update_d0i3c(struct device *dev, bool enable) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); u8 reg; int timeout = 50; @@ -189,8 +187,7 @@ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) static irqreturn_t skl_interrupt(int irq, void *dev_id) { - struct hdac_ext_bus *ebus = dev_id; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_id; u32 status; if (!pm_runtime_active(bus->dev)) @@ -219,8 +216,7 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) static irqreturn_t skl_threaded_handler(int irq, void *dev_id) { - struct hdac_ext_bus *ebus = dev_id; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_id; u32 status; status = snd_hdac_chip_readl(bus, INTSTS); @@ -230,16 +226,15 @@ static irqreturn_t skl_threaded_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) +static int skl_acquire_irq(struct hdac_bus *bus, int do_disconnect) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); int ret; ret = request_threaded_irq(skl->pci->irq, skl_interrupt, skl_threaded_handler, IRQF_SHARED, - KBUILD_MODNAME, ebus); + KBUILD_MODNAME, bus); if (ret) { dev_err(bus->dev, "unable to grab IRQ %d, disabling device\n", @@ -256,21 +251,20 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) static int skl_suspend_late(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); return skl_suspend_late_dsp(skl); } #ifdef CONFIG_PM -static int _skl_suspend(struct hdac_ext_bus *ebus) +static int _skl_suspend(struct hdac_bus *bus) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); struct pci_dev *pci = to_pci_dev(bus->dev); int ret; - snd_hdac_ext_bus_link_power_down_all(ebus); + snd_hdac_ext_bus_link_power_down_all(bus); ret = skl_suspend_dsp(skl); if (ret < 0) @@ -287,10 +281,9 @@ static int _skl_suspend(struct hdac_ext_bus *ebus) return 0; } -static int _skl_resume(struct hdac_ext_bus *ebus) +static int _skl_resume(struct hdac_bus *bus) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); skl_init_pci(skl); skl_init_chip(bus, true); @@ -306,9 +299,8 @@ static int _skl_resume(struct hdac_ext_bus *ebus) static int skl_suspend(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); int ret = 0; /* @@ -317,15 +309,15 @@ static int skl_suspend(struct device *dev) */ if (skl->supend_active) { /* turn off the links and stop the CORB/RIRB DMA if it is On */ - snd_hdac_ext_bus_link_power_down_all(ebus); + snd_hdac_ext_bus_link_power_down_all(bus); - if (ebus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(&ebus->bus); + if (bus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(bus); enable_irq_wake(bus->irq); pci_save_state(pci); } else { - ret = _skl_suspend(ebus); + ret = _skl_suspend(bus); if (ret < 0) return ret; skl->skl_sst->fw_loaded = false; @@ -344,9 +336,8 @@ static int skl_suspend(struct device *dev) static int skl_resume(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); struct hdac_ext_link *hlink = NULL; int ret; @@ -366,32 +357,32 @@ static int skl_resume(struct device *dev) */ if (skl->supend_active) { pci_restore_state(pci); - snd_hdac_ext_bus_link_power_up_all(ebus); + snd_hdac_ext_bus_link_power_up_all(bus); disable_irq_wake(bus->irq); /* * turn On the links which are On before active suspend * and start the CORB/RIRB DMA if On before * active suspend. */ - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { if (hlink->ref_count) snd_hdac_ext_bus_link_power_up(hlink); } - if (ebus->cmd_dma_state) - snd_hdac_bus_init_cmd_io(&ebus->bus); ret = 0; + if (bus->cmd_dma_state) + snd_hdac_bus_init_cmd_io(bus); } else { - ret = _skl_resume(ebus); + ret = _skl_resume(bus); /* turn off the links which are off before suspend */ - list_for_each_entry(hlink, &ebus->hlink_list, list) { + list_for_each_entry(hlink, &bus->hlink_list, list) { if (!hlink->ref_count) snd_hdac_ext_bus_link_power_down(hlink); } - if (!ebus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(&ebus->bus); + if (!bus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(bus); } return ret; @@ -402,23 +393,21 @@ static int skl_resume(struct device *dev) static int skl_runtime_suspend(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); dev_dbg(bus->dev, "in %s\n", __func__); - return _skl_suspend(ebus); + return _skl_suspend(bus); } static int skl_runtime_resume(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); dev_dbg(bus->dev, "in %s\n", __func__); - return _skl_resume(ebus); + return _skl_resume(bus); } #endif /* CONFIG_PM */ @@ -431,20 +420,19 @@ static const struct dev_pm_ops skl_pm = { /* * destructor */ -static int skl_free(struct hdac_ext_bus *ebus) +static int skl_free(struct hdac_bus *bus) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); skl->init_done = 0; /* to be sure */ - snd_hdac_ext_stop_streams(ebus); + snd_hdac_ext_stop_streams(bus); if (bus->irq >= 0) - free_irq(bus->irq, (void *)ebus); + free_irq(bus->irq, (void *)bus); snd_hdac_bus_free_stream_pages(bus); - snd_hdac_stream_free_all(ebus); - snd_hdac_link_free_all(ebus); + snd_hdac_stream_free_all(bus); + snd_hdac_link_free_all(bus); if (bus->remap_addr) iounmap(bus->remap_addr); @@ -452,11 +440,11 @@ static int skl_free(struct hdac_ext_bus *ebus) pci_release_regions(skl->pci); pci_disable_device(skl->pci); - snd_hdac_ext_bus_exit(ebus); + snd_hdac_ext_bus_exit(bus); cancel_work_sync(&skl->probe_work); if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) - snd_hdac_i915_exit(&ebus->bus); + snd_hdac_i915_exit(bus); return 0; } @@ -480,8 +468,8 @@ static struct skl_ssp_clk skl_ssp_clks[] = { static int skl_find_machine(struct skl *skl, void *driver_data) { + struct hdac_bus *bus = skl_to_bus(skl); struct snd_soc_acpi_mach *mach = driver_data; - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct skl_machine_pdata *pdata; mach = snd_soc_acpi_find_machine(mach); @@ -504,7 +492,7 @@ static int skl_find_machine(struct skl *skl, void *driver_data) static int skl_machine_device_register(struct skl *skl) { - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct snd_soc_acpi_mach *mach = skl->mach; struct platform_device *pdev; int ret; @@ -538,7 +526,7 @@ static void skl_machine_device_unregister(struct skl *skl) static int skl_dmic_device_register(struct skl *skl) { - struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct platform_device *pdev; int ret; @@ -637,9 +625,8 @@ static void skl_clock_device_unregister(struct skl *skl) /* * Probe the given codec address */ -static int probe_codec(struct hdac_ext_bus *ebus, int addr) +static int probe_codec(struct hdac_bus *bus, int addr) { - struct hdac_bus *bus = ebus_to_hbus(ebus); unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res = -1; @@ -652,13 +639,12 @@ static int probe_codec(struct hdac_ext_bus *ebus, int addr) return -EIO; dev_dbg(bus->dev, "codec #%d probed OK\n", addr); - return snd_hdac_ext_bus_device_init(ebus, addr); + return snd_hdac_ext_bus_device_init(bus, addr); } /* Codec initialization */ -static void skl_codec_create(struct hdac_ext_bus *ebus) +static void skl_codec_create(struct hdac_bus *bus) { - struct hdac_bus *bus = ebus_to_hbus(ebus); int c, max_slots; max_slots = HDA_MAX_CODECS; @@ -666,7 +652,7 @@ static void skl_codec_create(struct hdac_ext_bus *ebus) /* First try to probe all given codec slots */ for (c = 0; c < max_slots; c++) { if ((bus->codec_mask & (1 << c))) { - if (probe_codec(ebus, c) < 0) { + if (probe_codec(bus, c) < 0) { /* * Some BIOSen give you wrong codec addresses * that don't exist @@ -716,8 +702,7 @@ static int skl_i915_init(struct hdac_bus *bus) static void skl_probe_work(struct work_struct *work) { struct skl *skl = container_of(work, struct skl, probe_work); - struct hdac_ext_bus *ebus = &skl->ebus; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct hdac_ext_link *hlink = NULL; int err; @@ -738,7 +723,7 @@ static void skl_probe_work(struct work_struct *work) dev_info(bus->dev, "no hda codecs found!\n"); /* create codec instances */ - skl_codec_create(ebus); + skl_codec_create(bus); /* register platform dai and controls */ err = skl_platform_register(bus->dev); @@ -767,8 +752,8 @@ static void skl_probe_work(struct work_struct *work) /* * we are done probing so decrement link counts */ - list_for_each_entry(hlink, &ebus->hlink_list, list) - snd_hdac_ext_bus_link_put(ebus, hlink); + list_for_each_entry(hlink, &bus->hlink_list, list) + snd_hdac_ext_bus_link_put(bus, hlink); /* configure PM */ pm_runtime_put_noidle(bus->dev); @@ -790,7 +775,7 @@ static int skl_create(struct pci_dev *pci, struct skl **rskl) { struct skl *skl; - struct hdac_ext_bus *ebus; + struct hdac_bus *bus; int err; @@ -805,23 +790,22 @@ static int skl_create(struct pci_dev *pci, pci_disable_device(pci); return -ENOMEM; } - ebus = &skl->ebus; - snd_hdac_ext_bus_init(ebus, &pci->dev, &bus_core_ops, io_ops); - ebus->bus.use_posbuf = 1; + + bus = skl_to_bus(skl); + snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops); + bus->use_posbuf = 1; skl->pci = pci; INIT_WORK(&skl->probe_work, skl_probe_work); - - ebus->bus.bdl_pos_adj = 0; + bus->bdl_pos_adj = 0; *rskl = skl; return 0; } -static int skl_first_init(struct hdac_ext_bus *ebus) +static int skl_first_init(struct hdac_bus *bus) { - struct skl *skl = ebus_to_skl(ebus); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct skl *skl = bus_to_skl(bus); struct pci_dev *pci = skl->pci; int err; unsigned short gcap; @@ -842,7 +826,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus) snd_hdac_bus_parse_capabilities(bus); - if (skl_acquire_irq(ebus, 0) < 0) + if (skl_acquire_irq(bus, 0) < 0) return -EBUSY; pci_set_master(pci); @@ -866,14 +850,14 @@ static int skl_first_init(struct hdac_ext_bus *ebus) if (!pb_streams && !cp_streams) return -EIO; - ebus->num_streams = cp_streams + pb_streams; + bus->num_streams = cp_streams + pb_streams; /* initialize streams */ snd_hdac_ext_stream_init_all - (ebus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); + (bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); start_idx = cp_streams; snd_hdac_ext_stream_init_all - (ebus, start_idx, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); + (bus, start_idx, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); err = snd_hdac_bus_alloc_stream_pages(bus); if (err < 0) @@ -889,7 +873,6 @@ static int skl_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { struct skl *skl; - struct hdac_ext_bus *ebus = NULL; struct hdac_bus *bus = NULL; int err; @@ -898,10 +881,9 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) return err; - ebus = &skl->ebus; - bus = ebus_to_hbus(ebus); + bus = skl_to_bus(skl); - err = skl_first_init(ebus); + err = skl_first_init(bus); if (err < 0) goto out_free; @@ -922,7 +904,7 @@ static int skl_probe(struct pci_dev *pci, skl_nhlt_update_topology_bin(skl); - pci_set_drvdata(skl->pci, ebus); + pci_set_drvdata(skl->pci, bus); /* check if dsp is there */ if (bus->ppcap) { @@ -944,7 +926,7 @@ static int skl_probe(struct pci_dev *pci, skl->skl_sst->clock_power_gating = skl_clock_power_gating; } if (bus->mlcap) - snd_hdac_ext_bus_get_ml_capabilities(ebus); + snd_hdac_ext_bus_get_ml_capabilities(bus); snd_hdac_bus_stop_chip(bus); @@ -964,31 +946,30 @@ static int skl_probe(struct pci_dev *pci, out_nhlt_free: skl_nhlt_free(skl->nhlt); out_free: - skl_free(ebus); + skl_free(bus); return err; } static void skl_shutdown(struct pci_dev *pci) { - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); struct hdac_stream *s; struct hdac_ext_stream *stream; struct skl *skl; - if (ebus == NULL) + if (!bus) return; - skl = ebus_to_skl(ebus); + skl = bus_to_skl(bus); if (!skl->init_done) return; - snd_hdac_ext_stop_streams(ebus); + snd_hdac_ext_stop_streams(bus); list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); - snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_ext_stream_decouple(bus, stream, false); } snd_hdac_bus_stop_chip(bus); @@ -996,15 +977,15 @@ static void skl_shutdown(struct pci_dev *pci) static void skl_remove(struct pci_dev *pci) { - struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = pci_get_drvdata(pci); + struct skl *skl = bus_to_skl(bus); release_firmware(skl->tplg); pm_runtime_get_noresume(&pci->dev); /* codec removal, invoke bus_device_remove */ - snd_hdac_ext_bus_device_remove(ebus); + snd_hdac_ext_bus_device_remove(bus); skl->debugfs = NULL; skl_platform_unregister(&pci->dev); @@ -1014,7 +995,7 @@ static void skl_remove(struct pci_dev *pci) skl_clock_device_unregister(skl); skl_nhlt_remove_sysfs(skl); skl_nhlt_free(skl->nhlt); - skl_free(ebus); + skl_free(bus); dev_set_drvdata(&pci->dev, NULL); } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 0d5375cbcf6ec3..78aa8bdcb61929 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -71,7 +71,7 @@ struct skl_fw_config { }; struct skl { - struct hdac_ext_bus ebus; + struct hdac_bus hbus; struct pci_dev *pci; unsigned int init_done:1; /* delayed init status */ @@ -105,9 +105,8 @@ struct skl { struct snd_soc_acpi_mach *mach; }; -#define skl_to_ebus(s) (&(s)->ebus) -#define ebus_to_skl(sbus) \ - container_of(sbus, struct skl, sbus) +#define skl_to_bus(s) (&(s)->hbus) +#define bus_to_skl(bus) container_of(bus, struct skl, hbus) /* to pass dai dma data */ struct skl_dma_params { From a8b18e02aac923a5a2a47070de5d4e59ad6b0abe Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:35 +0800 Subject: [PATCH 269/285] ALSA: hdac: Remove usage of struct hdac_ext_driver, use hdac_driver instead This patch removes the hdac_ext_driver structure. The legacy and enhanced HDaudio capabilities can be handled in a backward-compatible way without separate definitions. Signed-off-by: Rakesh Ughreja --- include/sound/hdaudio.h | 5 +++++ include/sound/hdaudio_ext.h | 17 ++--------------- sound/hda/ext/hdac_ext_bus.c | 30 ++++++++++++++---------------- sound/soc/codecs/hdac_hdmi.c | 12 +++++------- 4 files changed, 26 insertions(+), 38 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 17ab1118cad57c..62dd486e7d4855 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -188,6 +188,11 @@ struct hdac_driver { const struct hda_device_id *id_table; int (*match)(struct hdac_device *dev, struct hdac_driver *drv); void (*unsol_event)(struct hdac_device *dev, unsigned int event); + + /* fields used by ext bus APIs */ + int (*probe)(struct hdac_device *dev); + int (*remove)(struct hdac_device *dev); + void (*shutdown)(struct hdac_device *dev); }; #define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index e5b0cd1ade19c2..3c302477750b8c 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -160,20 +160,7 @@ struct hdac_ext_dma_params { u8 stream_tag; }; -/* - * HD-audio codec base driver - */ -struct hdac_ext_driver { - struct hdac_driver hdac; - - int (*probe)(struct hdac_device *dev); - int (*remove)(struct hdac_device *dev); - void (*shutdown)(struct hdac_device *dev); -}; - -int snd_hda_ext_driver_register(struct hdac_ext_driver *drv); -void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv); - -#define to_ehdac_driver(_drv) container_of(_drv, struct hdac_ext_driver, hdac) +int snd_hda_ext_driver_register(struct hdac_driver *drv); +void snd_hda_ext_driver_unregister(struct hdac_driver *drv); #endif /* __SOUND_HDAUDIO_EXT_H */ diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 77547ede9ae84c..52f07766fff327 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -200,12 +200,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove); #define dev_to_hdac(dev) (container_of((dev), \ struct hdac_device, dev)) -static inline struct hdac_ext_driver *get_edrv(struct device *dev) +static inline struct hdac_driver *get_hdrv(struct device *dev) { struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver); - struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv); - - return edrv; + return hdrv; } static inline struct hdac_device *get_hdev(struct device *dev) @@ -216,17 +214,17 @@ static inline struct hdac_device *get_hdev(struct device *dev) static int hda_ext_drv_probe(struct device *dev) { - return (get_edrv(dev))->probe(get_hdev(dev)); + return (get_hdrv(dev))->probe(get_hdev(dev)); } static int hdac_ext_drv_remove(struct device *dev) { - return (get_edrv(dev))->remove(get_hdev(dev)); + return (get_hdrv(dev))->remove(get_hdev(dev)); } static void hdac_ext_drv_shutdown(struct device *dev) { - return (get_edrv(dev))->shutdown(get_hdev(dev)); + return (get_hdrv(dev))->shutdown(get_hdev(dev)); } /** @@ -234,20 +232,20 @@ static void hdac_ext_drv_shutdown(struct device *dev) * * @drv: ext hda driver structure */ -int snd_hda_ext_driver_register(struct hdac_ext_driver *drv) +int snd_hda_ext_driver_register(struct hdac_driver *drv) { - drv->hdac.type = HDA_DEV_ASOC; - drv->hdac.driver.bus = &snd_hda_bus_type; + drv->type = HDA_DEV_ASOC; + drv->driver.bus = &snd_hda_bus_type; /* we use default match */ if (drv->probe) - drv->hdac.driver.probe = hda_ext_drv_probe; + drv->driver.probe = hda_ext_drv_probe; if (drv->remove) - drv->hdac.driver.remove = hdac_ext_drv_remove; + drv->driver.remove = hdac_ext_drv_remove; if (drv->shutdown) - drv->hdac.driver.shutdown = hdac_ext_drv_shutdown; + drv->driver.shutdown = hdac_ext_drv_shutdown; - return driver_register(&drv->hdac.driver); + return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); @@ -256,8 +254,8 @@ EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); * * @drv: ext hda driver structure */ -void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv) +void snd_hda_ext_driver_unregister(struct hdac_driver *drv) { - driver_unregister(&drv->hdac.driver); + driver_unregister(&drv->driver); } EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister); diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index c3ccc8d9c91d10..3e3a2a9ef3108d 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -2186,14 +2186,12 @@ static const struct hda_device_id hdmi_list[] = { MODULE_DEVICE_TABLE(hdaudio, hdmi_list); -static struct hdac_ext_driver hdmi_driver = { - . hdac = { - .driver = { - .name = "HDMI HDA Codec", - .pm = &hdac_hdmi_pm, - }, - .id_table = hdmi_list, +static struct hdac_driver hdmi_driver = { + .driver = { + .name = "HDMI HDA Codec", + .pm = &hdac_hdmi_pm, }, + .id_table = hdmi_list, .probe = hdac_hdmi_dev_probe, .remove = hdac_hdmi_dev_remove, }; From c45d5dd646a5aaacf09ccef084c493c4ff28b579 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:36 +0800 Subject: [PATCH 270/285] ALSA: hda: split snd_hda_codec_new function Split snd_hda_codec_new into two separate functions. snd_hda_codec_device_init allocates memory and registers with bus. snd_hda_codec_device_new initialializes the fields and performs snd_device_new. This enables reuse of legacy HDA codec drivers as ASoC codec drivers. In addition mark some functions with EXPORT_SYMBOL_GPL so that it can be called by ASoC wrapper around the legacy HDA driver (hdac_hda). Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- sound/pci/hda/hda_codec.c | 68 +++++++++++++++++++++++++++++---------- sound/pci/hda/hda_codec.h | 2 ++ 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5bc3a7468e1716..76f4f097e83eb7 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -856,6 +856,39 @@ static void snd_hda_codec_dev_release(struct device *dev) kfree(codec); } +#define DEV_NAME_LEN 31 + +static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec **codecp) +{ + char name[DEV_NAME_LEN]; + struct hda_codec *codec; + int err; + + dev_dbg(card->dev, "%s: entry\n", __func__); + + if (snd_BUG_ON(!bus)) + return -EINVAL; + if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) + return -EINVAL; + + codec = kzalloc(sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + sprintf(name, "hdaudioC%dD%d", card->number, codec_addr); + err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr); + if (err < 0) { + kfree(codec); + return err; + } + + codec->core.type = HDA_DEV_LEGACY; + *codecp = codec; + + return err; +} + /** * snd_hda_codec_new - create a HDA codec * @bus: the bus to assign @@ -867,7 +900,19 @@ static void snd_hda_codec_dev_release(struct device *dev) int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp) { - struct hda_codec *codec; + int ret; + + ret = snd_hda_codec_device_init(bus, card, codec_addr, codecp); + if (ret < 0) + return ret; + + return snd_hda_codec_device_new(bus, card, codec_addr, *codecp); +} +EXPORT_SYMBOL_GPL(snd_hda_codec_new); + +int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec *codec) +{ char component[31]; hda_nid_t fg; int err; @@ -877,25 +922,14 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, .dev_free = snd_hda_codec_dev_free, }; + dev_dbg(card->dev, "%s: entry\n", __func__); + if (snd_BUG_ON(!bus)) return -EINVAL; if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) return -EINVAL; - codec = kzalloc(sizeof(*codec), GFP_KERNEL); - if (!codec) - return -ENOMEM; - - sprintf(component, "hdaudioC%dD%d", card->number, codec_addr); - err = snd_hdac_device_init(&codec->core, &bus->core, component, - codec_addr); - if (err < 0) { - kfree(codec); - return err; - } - codec->core.dev.release = snd_hda_codec_dev_release; - codec->core.type = HDA_DEV_LEGACY; codec->core.exec_verb = codec_exec_verb; codec->bus = bus; @@ -955,15 +989,13 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, if (err < 0) goto error; - if (codecp) - *codecp = codec; return 0; error: put_device(hda_codec_dev(codec)); return err; } -EXPORT_SYMBOL_GPL(snd_hda_codec_new); +EXPORT_SYMBOL_GPL(snd_hda_codec_device_new); /** * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults @@ -2979,6 +3011,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) sync_power_up_states(codec); return 0; } +EXPORT_SYMBOL_GPL(snd_hda_codec_build_controls); /* * PCM stuff @@ -3184,6 +3217,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec) return 0; } +EXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms); /* assign all PCMs of the given codec */ int snd_hda_codec_build_pcms(struct hda_codec *codec) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 681c360f29f9d6..8bbedf7f3f5456 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -307,6 +307,8 @@ struct hda_codec { */ 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_device_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec *codec); int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_update_widgets(struct hda_codec *codec); From dce37cc70039b4812bf019db089737bbc91573ff Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:37 +0800 Subject: [PATCH 271/285] ALSA: hdac: remove memory allocation from snd_hdac_ext_bus_device_init Remove memory allocation within snd_hdac_ext_bus_device_init, to make its behaviour identical to snd_hdac_bus_device_init. So that caller can allocate the parent data structure containing hdac_device. This API change helps in reusing the legacy HDA codec drivers with ASoC platform drivers. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- include/sound/hdaudio_ext.h | 3 ++- sound/hda/ext/hdac_ext_bus.c | 8 ++------ sound/soc/intel/skylake/skl.c | 8 +++++++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 3c302477750b8c..c188b801239f98 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -9,7 +9,8 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_io_ops *io_ops); void snd_hdac_ext_bus_exit(struct hdac_bus *bus); -int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr); +int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr, + struct hdac_device *hdev); void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev); void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus); diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 52f07766fff327..1eb58244688e78 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -135,16 +135,12 @@ static void default_release(struct device *dev) * * Returns zero for success or a negative error code. */ -int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr) +int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr, + struct hdac_device *hdev) { - struct hdac_device *hdev = NULL; char name[15]; int ret; - hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); - if (!hdev) - return -ENOMEM; - hdev->bus = bus; snprintf(name, sizeof(name), "ehdaudio%dD%d", bus->idx, addr); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 4be003540202a6..29c32cfa5247cf 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -630,6 +630,8 @@ static int probe_codec(struct hdac_bus *bus, int addr) unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res = -1; + struct skl *skl = bus_to_skl(bus); + struct hdac_device *hdev; mutex_lock(&bus->cmd_mutex); snd_hdac_bus_send_cmd(bus, cmd); @@ -639,7 +641,11 @@ static int probe_codec(struct hdac_bus *bus, int addr) return -EIO; dev_dbg(bus->dev, "codec #%d probed OK\n", addr); - return snd_hdac_ext_bus_device_init(bus, addr); + hdev = devm_kzalloc(&skl->pci->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + + return snd_hdac_ext_bus_device_init(bus, addr, hdev); } /* Codec initialization */ From 6d3b6f3371fb9c86888c4eeb5460a057c4ea4dde Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:38 +0800 Subject: [PATCH 272/285] ALSA: hdac: add extended ops in the hdac_bus Add extended ops in the hdac_bus to allow calling the ASoC HDAC library ops to reuse the legacy HDA codec drivers with ASoC framework. Extended ops are used by the legacy codec drivers to call into hdac_hda library, in the subsequent patches.. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- include/sound/hdaudio.h | 9 +++++++++ include/sound/hdaudio_ext.h | 3 ++- sound/hda/ext/hdac_ext_bus.c | 4 +++- sound/soc/intel/skylake/skl.c | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 62dd486e7d4855..78dd64f2672f49 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -213,6 +213,14 @@ struct hdac_bus_ops { int (*link_power)(struct hdac_bus *bus, bool enable); }; +/* + * ops used for ASoC HDA codec drivers + */ +struct hdac_ext_bus_ops { + int (*hdev_attach)(struct hdac_device *hdev); + int (*hdev_detach)(struct hdac_device *hdev); +}; + /* * Lowlevel I/O operators */ @@ -265,6 +273,7 @@ struct hdac_bus { struct device *dev; const struct hdac_bus_ops *ops; const struct hdac_io_ops *io_ops; + const struct hdac_ext_bus_ops *ext_ops; /* h/w resources */ unsigned long addr; diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index c188b801239f98..f34aced69ca803 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -6,7 +6,8 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_bus_ops *ops, - const struct hdac_io_ops *io_ops); + const struct hdac_io_ops *io_ops, + const struct hdac_ext_bus_ops *ext_ops); void snd_hdac_ext_bus_exit(struct hdac_bus *bus); int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr, diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 1eb58244688e78..9c37d9af3023f6 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -89,7 +89,8 @@ static const struct hdac_io_ops hdac_ext_default_io = { */ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_bus_ops *ops, - const struct hdac_io_ops *io_ops) + const struct hdac_io_ops *io_ops, + const struct hdac_ext_bus_ops *ext_ops) { int ret; static int idx; @@ -102,6 +103,7 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, if (ret < 0) return ret; + bus->ext_ops = ext_ops; INIT_LIST_HEAD(&bus->hlink_list); bus->idx = idx++; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 29c32cfa5247cf..313d733bc09440 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -798,7 +798,7 @@ static int skl_create(struct pci_dev *pci, } bus = skl_to_bus(skl); - snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops); + snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, NULL); bus->use_posbuf = 1; skl->pci = pci; INIT_WORK(&skl->probe_work, skl_probe_work); From ffde85191c04b2b065f7ad4e1ac552f9b5ba234c Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:39 +0800 Subject: [PATCH 273/285] ASoC: Intel: Boards: Machine driver for SKL+ w/ HDAudio codecs Add machine driver for Intel platforms (SKL/KBL/BXT/APL) with HDA and iDisp codecs. This patch adds support for only iDisp (HDMI/DP) codec. In the following patches support for HDA codecs will be added. This should work for other Intel platforms as well e.g. GLK,CNL however this series is not tested on all the platforms. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 8 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/skl_hda_dsp_common.c | 133 ++++++++++++++ sound/soc/intel/boards/skl_hda_dsp_common.h | 37 ++++ sound/soc/intel/boards/skl_hda_dsp_generic.c | 174 +++++++++++++++++++ sound/soc/intel/skylake/skl.h | 2 + 6 files changed, 356 insertions(+) create mode 100644 sound/soc/intel/boards/skl_hda_dsp_common.c create mode 100644 sound/soc/intel/boards/skl_hda_dsp_common.h create mode 100644 sound/soc/intel/boards/skl_hda_dsp_generic.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 0b3ee787bad7ef..80bb1268a21d64 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -314,6 +314,14 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for DA7219 + MAX98357A I2S audio codec. Say Y if you have such a device. + +config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH + tristate "SKL/KBL/BXT/APL with HDA Codecs" + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Intel platforms + SKL/KBL/BXT/APL with iDisp, HDA audio codecs. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 8c7d03abeff397..9481255989e7c7 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -21,6 +21,7 @@ snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o +snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-cnl-rt274-objs := cnl_rt274.o @@ -50,3 +51,4 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o +obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c new file mode 100644 index 00000000000000..4b453087fda641 --- /dev/null +++ b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2015-17 Intel Corporation. + */ + +/* + * Common functions used in different Intel machine drivers + */ +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" +#include "skl_hda_dsp_common.h" + +#define NAME_SIZE 32 + +int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device) +{ + char dai_name[NAME_SIZE]; + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + struct skl_hda_hdmi_pcm *pcm; + static int i = 1; /* hdmi codec dai name starts from index 1 */ + + pcm = devm_kzalloc(card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + snprintf(dai_name, sizeof(dai_name), "intel-hdmi-hifi%d", i++); + pcm->codec_dai = snd_soc_card_get_codec_dai(card, dai_name); + if (!pcm->codec_dai) + return -EINVAL; + + pcm->device = device; + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +/* skl_hda_digital audio interface glue - connects codec <--> CPU */ +struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS] = { + + /* Back End DAI links */ + { + .name = "iDisp1", + .id = 1, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 2, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 3, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "Analog Playback and Capture", + .id = 4, + .cpu_dai_name = "Analog CPU DAI", + .codec_name = "ehdaudio0D0", + .codec_dai_name = "Analog Codec DAI", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = NULL, + .no_pcm = 1, + }, + { + .name = "Digital Playback and Capture", + .id = 5, + .cpu_dai_name = "Digital CPU DAI", + .codec_name = "ehdaudio0D0", + .codec_dai_name = "Digital Codec DAI", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = NULL, + .no_pcm = 1, + }, +}; + +int skl_hda_hdmi_jack_init(struct snd_soc_card *card) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + struct skl_hda_hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err; + char jack_name[NAME_SIZE]; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &pcm->hdmi_jack, + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &pcm->hdmi_jack); + if (err < 0) + return err; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h new file mode 100644 index 00000000000000..dd25d8550ddbe3 --- /dev/null +++ b/sound/soc/intel/boards/skl_hda_dsp_common.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2015-17 Intel Corporation. + */ + +/* + * This file defines data structures used in Machine Driver for Intel + * platforms with HDA Codecs. + */ + +#ifndef __SOUND_SOC_HDA_DSP_COMMON_H +#define __SOUND_SOC_HDA_DSP_COMMON_H +#include +#include +#include +#include + +#define HDA_DSP_MAX_BE_DAI_LINKS 5 + +struct skl_hda_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + struct snd_soc_jack hdmi_jack; + int device; +}; + +struct skl_hda_private { + struct list_head hdmi_pcm_list; + int pcm_count; + const char *platform_name; +}; + +extern struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS]; +int skl_hda_hdmi_jack_init(struct snd_soc_card *card); +int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device); + +#endif /* __SOUND_SOC_HDA_DSP_COMMON_H */ diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c new file mode 100644 index 00000000000000..3fcaf127fe7971 --- /dev/null +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-17 Intel Corporation. + +/* + * Machine Driver for SKL/KBL/BXT/APL platforms with iDisp, HDA Codecs + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" +#include "skl_hda_dsp_common.h" + +static const struct snd_soc_dapm_widget skl_hda_widgets[] = { + SND_SOC_DAPM_HP("Analog Out", NULL), + SND_SOC_DAPM_MIC("Analog In", NULL), + SND_SOC_DAPM_HP("Alt Analog Out", NULL), + SND_SOC_DAPM_MIC("Alt Analog In", NULL), + SND_SOC_DAPM_SPK("Digital Out", NULL), + SND_SOC_DAPM_MIC("Digital In", NULL), +}; + +static const struct snd_soc_dapm_route skl_hda_map[] = { + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + + { "Analog Out", NULL, "Codec Output Pin1" }, + { "Digital Out", NULL, "Codec Output Pin2" }, + { "Alt Analog Out", NULL, "Codec Output Pin3" }, + + { "Codec Input Pin1", NULL, "Analog In" }, + { "Codec Input Pin2", NULL, "Digital In" }, + { "Codec Input Pin3", NULL, "Alt Analog In" }, + + /* CODEC BE connections */ + { "Analog Codec Playback", NULL, "Analog CPU Playback" }, + { "Analog CPU Playback", NULL, "codec0_out" }, + { "Digital Codec Playback", NULL, "Digital CPU Playback" }, + { "Digital CPU Playback", NULL, "codec1_out" }, + { "Alt Analog Codec Playback", NULL, "Alt Analog CPU Playback" }, + { "Alt Analog CPU Playback", NULL, "codec2_out" }, + + { "codec0_in", NULL, "Analog CPU Capture" }, + { "Analog CPU Capture", NULL, "Analog Codec Capture" }, + { "codec1_in", NULL, "Digital CPU Capture" }, + { "Digital CPU Capture", NULL, "Digital Codec Capture" }, + { "codec2_in", NULL, "Alt Analog CPU Capture" }, + { "Alt Analog CPU Capture", NULL, "Alt Analog Codec Capture" }, +}; + +static int skl_hda_card_late_probe(struct snd_soc_card *card) +{ + return skl_hda_hdmi_jack_init(card); +} + +static int +skl_hda_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + int ret = 0; + + dev_dbg(card->dev, "%s: dai link name - %s\n", __func__, link->name); + link->platform_name = ctx->platform_name; + link->nonatomic = 1; + + if (strstr(link->name, "HDMI")) + ret = skl_hda_hdmi_add_pcm(card, ctx->pcm_count); + ctx->pcm_count++; + + return ret; +} + +static struct snd_soc_card hda_soc_card = { + .name = "skl_hda_card", + .owner = THIS_MODULE, + .dai_link = skl_hda_be_dai_links, + .dapm_widgets = skl_hda_widgets, + .dapm_routes = skl_hda_map, + .add_dai_link = skl_hda_add_dai_link, + .fully_routed = true, + .late_probe = skl_hda_card_late_probe, +}; + +#define IDISP_DAI_COUNT 3 +#define IDISP_ROUTE_COUNT 3 +#define IDISP_CODEC_MASK 0x4 + +static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) +{ + + struct snd_soc_card *card = &hda_soc_card; + u32 codec_count, codec_mask; + int i, num_links, num_route; + + codec_mask = pdata->codec_mask; + codec_count = hweight_long(codec_mask); + + if (codec_count == 1 && pdata->codec_mask & IDISP_CODEC_MASK) { + num_links = IDISP_DAI_COUNT; + num_route = IDISP_ROUTE_COUNT; + } else if (codec_count == 2 && codec_mask & IDISP_CODEC_MASK) { + num_links = ARRAY_SIZE(skl_hda_be_dai_links); + num_route = ARRAY_SIZE(skl_hda_map), + card->dapm_widgets = skl_hda_widgets; + card->num_dapm_widgets = ARRAY_SIZE(skl_hda_widgets); + } else { + return -EINVAL; + } + + card->num_links = num_links; + card->num_dapm_routes = num_route; + + for (i = 0; i < num_links; i++) + skl_hda_be_dai_links[i].platform_name = pdata->platform; + + return 0; +} + +static int skl_hda_audio_probe(struct platform_device *pdev) +{ + struct skl_machine_pdata *pdata; + struct skl_hda_private *ctx; + int ret; + + dev_dbg(&pdev->dev, "%s: entry\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + pdata = dev_get_drvdata(&pdev->dev); + if (!pdata) + return -EINVAL; + + ret = skl_hda_fill_card_info(pdata); + if (ret < 0) + return ret; + + ctx->pcm_count = hda_soc_card.num_links; + ctx->platform_name = pdata->platform; + + hda_soc_card.dev = &pdev->dev; + snd_soc_card_set_drvdata(&hda_soc_card, ctx); + + return devm_snd_soc_register_card(&pdev->dev, &hda_soc_card); +} + +static struct platform_driver skl_hda_audio = { + .probe = skl_hda_audio_probe, + .driver = { + .name = "skl_hda_generic", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(skl_hda_audio) + +/* Module information */ +MODULE_DESCRIPTION("SKL/KBL/BXT/APL HDA Generic Machine driver"); +MODULE_AUTHOR("Rakesh Ughreja "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl_hda_generic"); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 78aa8bdcb61929..4105a9371b6425 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -117,6 +117,8 @@ struct skl_dma_params { struct skl_machine_pdata { u32 dmic_num; bool use_tplg_pcm; /* use dais and dai links from topology */ + const char *platform; + u32 codec_mask; }; struct skl_dsp_ops { From d0b2ca082370c28645445b52facffb854d0f06ad Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:40 +0800 Subject: [PATCH 274/285] ASoC: Intel: Skylake: Add entry in sst_acpi_mach for HDA codecs When no I2S based codec entries are found in the BIOS, check if there are any HDA codecs detected on the bus. Based on the number of codecs found take appropriate action in machine driver. If there are two HDA codecs i.e. iDisp + HDA found on the bus, register DAIs and DAI links for both. If only one codec i.e. iDisp is found then load only iDisp machine driver. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart Signed-off-by: Keyon Jie --- .../intel/common/soc-acpi-intel-bxt-match.c | 7 ++++ .../intel/common/soc-acpi-intel-cnl-match.c | 7 ++++ .../intel/common/soc-acpi-intel-kbl-match.c | 7 ++++ .../intel/common/soc-acpi-intel-skl-match.c | 7 ++++ sound/soc/intel/skylake/skl.c | 36 ++++++++++++++++--- 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index f39386e540d322..333ae36f3fb06e 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -51,6 +51,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", .asoc_plat_name = "0000:00:0e.0", }, + { + .id = "HDA_GEN", + .drv_name = "skl_hda_generic", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = NULL, + .quirk_data = NULL, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index ae3be064e75e99..60aee426a83382 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -33,6 +33,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .sof_tplg_filename = "intel/sof-cnl.tplg", .asoc_plat_name = "0000:00:0e.0", }, + { + .id = "HDA_GEN", + .drv_name = "skl_hda_generic", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = NULL, + .quirk_data = NULL, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c index 0ee173ca437dd3..81520ec8b042ec 100644 --- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -83,6 +83,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { .quirk_data = &kbl_7219_98357_codecs, .pdata = &skl_dmic_data, }, + { + .id = "HDA_GEN", + .drv_name = "skl_hda_generic", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = NULL, + .quirk_data = NULL, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-skl-match.c b/sound/soc/intel/common/soc-acpi-intel-skl-match.c index 0c9c0edd35b34d..df9010e03d8f48 100644 --- a/sound/soc/intel/common/soc-acpi-intel-skl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-skl-match.c @@ -39,6 +39,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[] = { .quirk_data = &skl_codecs, .pdata = &skl_dmic_data, }, + { + .id = "HDA_GEN", + .drv_name = "skl_hda_generic", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = NULL, + .quirk_data = NULL, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_skl_machines); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 313d733bc09440..52bb8c08102320 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -466,6 +466,23 @@ static struct skl_ssp_clk skl_ssp_clks[] = { {.name = "ssp5_sclkfs"}, }; +static struct snd_soc_acpi_mach *skl_find_hda_machine(struct skl *skl, + struct snd_soc_acpi_mach *machines) +{ + struct hdac_bus *bus = skl_to_bus(skl); + struct snd_soc_acpi_mach *mach; + + /* check if we have any codecs detected on bus */ + if (bus->codec_mask == 0) + return NULL; + + for (mach = machines; mach->id[0]; mach++) { + if (!strcmp(mach->id, "HDA_GEN")) + return mach; + } + return NULL; +} + static int skl_find_machine(struct skl *skl, void *driver_data) { struct hdac_bus *bus = skl_to_bus(skl); @@ -473,9 +490,13 @@ static int skl_find_machine(struct skl *skl, void *driver_data) struct skl_machine_pdata *pdata; mach = snd_soc_acpi_find_machine(mach); - if (mach == NULL) { - dev_err(bus->dev, "No matching machine driver found\n"); - return -ENODEV; + if (!mach) { + dev_dbg(bus->dev, "No matching I2S machine driver found\n"); + mach = skl_find_hda_machine(skl, driver_data); + if (!mach) { + dev_err(bus->dev, "No matching machine driver found\n"); + return -ENODEV; + } } skl->mach = mach; @@ -492,8 +513,9 @@ static int skl_find_machine(struct skl *skl, void *driver_data) static int skl_machine_device_register(struct skl *skl) { - struct hdac_bus *bus = skl_to_bus(skl); struct snd_soc_acpi_mach *mach = skl->mach; + struct hdac_bus *bus = skl_to_bus(skl); + struct skl_machine_pdata *pdata; struct platform_device *pdev; int ret; @@ -510,8 +532,12 @@ static int skl_machine_device_register(struct skl *skl) return -EIO; } - if (mach->pdata) + if (mach->pdata) { + pdata = (struct skl_machine_pdata *)mach->pdata; + pdata->platform = dev_name(bus->dev); + pdata->codec_mask = bus->codec_mask; dev_set_drvdata(&pdev->dev, mach->pdata); + } skl->i2s_dev = pdev; From e950ec5425f8eee3c03bc49253afd5c6b9ad923d Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:41 +0800 Subject: [PATCH 275/285] ASoC: Intel: Skylake: add HDA BE DAIs Add support for HDA BE DAIs in SKL platform driver. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/skylake/skl-pcm.c | 70 +++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index ca5f0d76197a6f..2afc7d0174b48d 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -32,6 +32,7 @@ #define HDA_MONO 1 #define HDA_STEREO 2 #define HDA_QUAD 4 +#define HDA_MAX 8 static const struct snd_pcm_hardware azx_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | @@ -541,7 +542,10 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, stream_tag = hdac_stream(link_dev)->stream_tag; /* set the stream tag in the codec dai dma params */ - snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); + else + snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -967,21 +971,63 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { - .name = "HD-Codec Pin", + .name = "Analog CPU DAI", .ops = &skl_link_dai_ops, .playback = { - .stream_name = "HD-Codec Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .stream_name = "Analog CPU Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { - .stream_name = "HD-Codec Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .stream_name = "Analog CPU Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "Alt Analog CPU DAI", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "Alt Analog CPU Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Alt Analog CPU Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "Digital CPU DAI", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "Digital CPU Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Digital CPU Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, }; From b56c360191779bfe7a33b33bf3d55514e04d433c Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:42 +0800 Subject: [PATCH 276/285] ASoC: Intel: Skylake: use hda_bus instead of hdac_bus Use hda_bus instead of hdac_bus in the SKL ASoC platform driver to enable reuse of legacy HDA codec drivers. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/skylake/skl.c | 10 +++++++++- sound/soc/intel/skylake/skl.h | 10 +++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 52bb8c08102320..971d737cf44252 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -36,6 +36,7 @@ #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "../../../pci/hda/hda_codec.h" /* * initialize the PCI registers @@ -665,7 +666,7 @@ static int probe_codec(struct hdac_bus *bus, int addr) mutex_unlock(&bus->cmd_mutex); if (res == -1) return -EIO; - dev_dbg(bus->dev, "codec #%d probed OK\n", addr); + dev_dbg(bus->dev, "codec #%d probed OK: %x\n", addr, res); hdev = devm_kzalloc(&skl->pci->dev, sizeof(*hdev), GFP_KERNEL); if (!hdev) @@ -808,6 +809,7 @@ static int skl_create(struct pci_dev *pci, { struct skl *skl; struct hdac_bus *bus; + struct hda_bus *hbus; int err; @@ -823,6 +825,7 @@ static int skl_create(struct pci_dev *pci, return -ENOMEM; } + hbus = skl_to_hbus(skl); bus = skl_to_bus(skl); snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, NULL); bus->use_posbuf = 1; @@ -830,6 +833,11 @@ static int skl_create(struct pci_dev *pci, INIT_WORK(&skl->probe_work, skl_probe_work); bus->bdl_pos_adj = 0; + mutex_init(&hbus->prepare_mutex); + hbus->pci = pci; + hbus->mixer_assigned = -1; + hbus->modelname = "sklbus"; + *rskl = skl; return 0; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4105a9371b6425..803cce47ffb29b 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -26,6 +26,7 @@ #include #include "skl-nhlt.h" #include "skl-ssp-clk.h" +#include "../../../pci/hda/hda_codec.h" #define SKL_SUSPEND_DELAY 2000 @@ -71,7 +72,7 @@ struct skl_fw_config { }; struct skl { - struct hdac_bus hbus; + struct hda_bus hbus; struct pci_dev *pci; unsigned int init_done:1; /* delayed init status */ @@ -105,8 +106,11 @@ struct skl { struct snd_soc_acpi_mach *mach; }; -#define skl_to_bus(s) (&(s)->hbus) -#define bus_to_skl(bus) container_of(bus, struct skl, hbus) +#define skl_to_bus(s) (&(s)->hbus.core) +#define bus_to_skl(bus) container_of(bus, struct skl, hbus.core) + +#define skl_to_hbus(s) (&(s)->hbus) +#define hbus_to_skl(hbus) container_of((hbus), struct skl, (hbus)) /* to pass dai dma data */ struct skl_dma_params { From 4292b059ef6a089c1de7dbd75488b2a93187dc2c Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:43 +0800 Subject: [PATCH 277/285] ASoC: hdac_hda: add asoc extension for legacy HDA codec drivers This patch adds a kernel module which is used by the legacy HDA codec drivers as library. This implements hdac_ext_bus_ops to enable the reuse of legacy HDA codec drivers with ASoC platform drivers. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- sound/pci/hda/hda_bind.c | 12 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/hdac_hda.c | 469 +++++++++++++++++++++++++++++++++ sound/soc/codecs/hdac_hda.h | 24 ++ sound/soc/intel/boards/Kconfig | 1 + sound/soc/intel/skylake/skl.c | 47 +++- 7 files changed, 556 insertions(+), 4 deletions(-) create mode 100644 sound/soc/codecs/hdac_hda.c create mode 100644 sound/soc/codecs/hdac_hda.h diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index d361bb77ca00d1..b1440a6c515f49 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -81,6 +81,12 @@ static int hda_codec_driver_probe(struct device *dev) hda_codec_patch_t patch; int err; + if (codec->bus->core.ext_ops) { + if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach)) + return -EINVAL; + return codec->bus->core.ext_ops->hdev_attach(&codec->core); + } + if (WARN_ON(!codec->preset)) return -EINVAL; @@ -134,6 +140,12 @@ static int hda_codec_driver_remove(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); + if (codec->bus->core.ext_ops) { + if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach)) + return -EINVAL; + return codec->bus->core.ext_ops->hdev_detach(&codec->core); + } + if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index fbacaa0a724bd4..dc0e50748c8ff0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -82,6 +82,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ES7134 select SND_SOC_GTM601 select SND_SOC_HDAC_HDMI + select SND_SOC_HDAC_HDA select SND_SOC_ICS43432 select SND_SOC_INNO_RK3036 select SND_SOC_ISABELLE if I2C @@ -610,6 +611,10 @@ config SND_SOC_HDAC_HDMI select SND_PCM_ELD select HDMI +config SND_SOC_HDAC_HDA + tristate + select SND_HDA + config SND_SOC_ICS43432 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 40480a5243ff2e..81355f55102528 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -77,6 +77,7 @@ snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o snd-soc-gtm601-objs := gtm601.o snd-soc-hdac-hdmi-objs := hdac_hdmi.o +snd-soc-hdac-hda-objs := hdac_hda.o snd-soc-ics43432-objs := ics43432.o snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-isabelle-objs := isabelle.o @@ -331,6 +332,7 @@ obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o +obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c new file mode 100644 index 00000000000000..672af726a2d9ab --- /dev/null +++ b/sound/soc/codecs/hdac_hda.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-17 Intel Corporation. + +/* + * hdac_hda.c - ASoC extensions to reuse the legacy HDA codec drivers + * with ASoC platform drivers. These APIs are called by the legacy HDA + * codec drivers using hdac_ext_bus_ops ops. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../hda/local.h" +#include "../../pci/hda/hda_codec.h" +#include "hdac_hda.h" + +#define HDAC_ANALOG_DAI_ID 0 +#define HDAC_DIGITAL_DAI_ID 1 +#define HDAC_ALT_ANALOG_DAI_ID 2 + +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static int hdac_hda_dai_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static void hdac_hda_dai_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width); +static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, + struct snd_soc_dai *dai); + +static struct snd_soc_dai_ops hdac_hda_dai_ops = { + .startup = hdac_hda_dai_open, + .shutdown = hdac_hda_dai_close, + .prepare = hdac_hda_dai_prepare, + .hw_free = hdac_hda_dai_hw_free, + .set_tdm_slot = hdac_hda_dai_set_tdm_slot, +}; + +static struct snd_soc_dai_driver hdac_hda_dais[] = { +{ + .id = HDAC_ANALOG_DAI_ID, + .name = "Analog Codec DAI", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "Analog Codec Playback", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Analog Codec Capture", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_DIGITAL_DAI_ID, + .name = "Digital Codec DAI", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "Digital Codec Playback", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Digital Codec Capture", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_ALT_ANALOG_DAI_ID, + .name = "Alt Analog Codec DAI", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "Alt Analog Codec Playback", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Alt Analog Codec Capture", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +} + +}; + +static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hdac_hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = &hda_pvt->pcm[dai->id]; + if (tx_mask) + pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; + else + pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; + + return 0; +} + +static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hda_pcm_stream *hda_stream; + struct hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return -EINVAL; + + hda_stream = &pcm->stream[substream->stream]; + snd_hda_codec_cleanup(&hda_pvt->codec, hda_stream, substream); + + return 0; +} + +static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hdac_device *hdev; + struct hda_pcm_stream *hda_stream; + unsigned int format_val; + struct hda_pcm *pcm; + int ret = 0; + + hda_pvt = snd_soc_component_get_drvdata(component); + hdev = &hda_pvt->codec.core; + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return -EINVAL; + + hda_stream = &pcm->stream[substream->stream]; + + format_val = snd_hdac_calc_stream_format(runtime->rate, + runtime->channels, + runtime->format, + hda_stream->maxbps, + 0); + if (!format_val) { + dev_err(&hdev->dev, + "invalid format_val, rate=%d, ch=%d, format=%d\n", + runtime->rate, runtime->channels, runtime->format); + return -EINVAL; + } + + ret = snd_hda_codec_prepare(&hda_pvt->codec, hda_stream, + hda_pvt->pcm[dai->id].stream_tag[substream->stream], + format_val, substream); + if (ret < 0) { + dev_err(&hdev->dev, "codec prepare failed %d\n", ret); + return ret; + } + + return ret; +} + +static int hdac_hda_dai_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hda_pcm_stream *hda_stream; + struct hda_pcm *pcm; + int ret = -ENODEV; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return -EINVAL; + + snd_hda_codec_pcm_get(pcm); + + hda_stream = &pcm->stream[substream->stream]; + if (hda_stream->ops.open) + ret = hda_stream->ops.open(hda_stream, &hda_pvt->codec, + substream); + return ret; +} + +static void hdac_hda_dai_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hda_pcm_stream *hda_stream; + struct hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return; + + hda_stream = &pcm->stream[substream->stream]; + if (hda_stream->ops.close) + hda_stream->ops.close(hda_stream, &hda_pvt->codec, substream); + + snd_hda_codec_pcm_put(pcm); +} + +static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, + struct snd_soc_dai *dai) +{ + struct hda_codec *hcodec = &hda_pvt->codec; + struct hda_pcm *cpcm; + const char *pcm_name; + + if (dai->id == HDAC_ANALOG_DAI_ID) + pcm_name = "Analog"; + else if (dai->id == HDAC_DIGITAL_DAI_ID) + pcm_name = "Digital"; + else + pcm_name = "Alt Analog"; + + list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { + if (strpbrk(cpcm->name, pcm_name)) + return cpcm; + } + + dev_err(&hcodec->core.dev, "didn't find PCM for DAI %s\n", dai->name); + return NULL; +} + +static int hdac_hda_codec_probe(struct snd_soc_component *component) +{ + struct hdac_hda_priv *hda_pvt = + snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct hdac_device *hdev = &hda_pvt->codec.core; + struct hda_codec *hcodec = &hda_pvt->codec; + struct hdac_ext_link *hlink; + hda_codec_patch_t patch; + int ret; + + hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return -EIO; + } + + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + ret = snd_hda_codec_device_new(hcodec->bus, + component->card->snd_card, hdev->addr, hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "failed to create hda codec %d\n", ret); + return ret; + } + + /* + * snd_hda_codec_device_new decrements the usage count so call get pm + * else the device will be powered off + */ + pm_runtime_get_noresume(&hdev->dev); + + hcodec->bus->card = dapm->card->snd_card; + + ret = snd_hda_codec_set_name(hcodec, hcodec->preset->name); + if (ret < 0) { + dev_err(&hdev->dev, "name failed %s\n", hcodec->preset->name); + return ret; + } + + ret = snd_hdac_regmap_init(&hcodec->core); + if (ret < 0) { + dev_err(&hdev->dev, "regmap init failed\n"); + return ret; + } + + patch = (hda_codec_patch_t)hcodec->preset->driver_data; + if (patch) { + ret = patch(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "patch failed %d\n", ret); + return ret; + } + } else { + dev_dbg(&hdev->dev, "no patch file found\n"); + } + + ret = snd_hda_codec_parse_pcms(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret); + return ret; + } + + ret = snd_hda_codec_build_controls(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to create controls %d\n", ret); + return ret; + } + + hcodec->core.lazy_cache = true; + + /* + * hdac_device core already sets the state to active and calls + * get_noresume. So enable runtime and set the device to suspend. + * pm_runtime_enable is also called during codec registeration + */ + pm_runtime_put(&hdev->dev); + pm_runtime_suspend(&hdev->dev); + + return 0; +} + +static void hdac_hda_codec_remove(struct snd_soc_component *component) +{ + struct hdac_hda_priv *hda_pvt = + snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = &hda_pvt->codec.core; + struct hdac_ext_link *hlink = NULL; + + hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return; + } + + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + pm_runtime_disable(&hdev->dev); +} + +static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = { + {"AIF1TX", NULL, "Codec Input Pin1"}, + {"AIF2TX", NULL, "Codec Input Pin2"}, + {"AIF3TX", NULL, "Codec Input Pin3"}, + + {"Codec Output Pin1", NULL, "AIF1RX"}, + {"Codec Output Pin2", NULL, "AIF2RX"}, + {"Codec Output Pin3", NULL, "AIF3RX"}, +}; + +static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0, + SND_SOC_NOPM, 0, 0), + + /* Input Pins */ + SND_SOC_DAPM_INPUT("Codec Input Pin1"), + SND_SOC_DAPM_INPUT("Codec Input Pin2"), + SND_SOC_DAPM_INPUT("Codec Input Pin3"), + + /* Output Pins */ + SND_SOC_DAPM_OUTPUT("Codec Output Pin1"), + SND_SOC_DAPM_OUTPUT("Codec Output Pin2"), + SND_SOC_DAPM_OUTPUT("Codec Output Pin3"), +}; + +static const struct snd_soc_component_driver hdac_hda_codec = { + .probe = hdac_hda_codec_probe, + .remove = hdac_hda_codec_remove, + .idle_bias_on = false, + .dapm_widgets = hdac_hda_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets), + .dapm_routes = hdac_hda_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes), +}; + +static int hdac_hda_dev_probe(struct hdac_device *hdev) +{ + struct hdac_ext_link *hlink; + struct hdac_hda_priv *hda_pvt; + int ret; + + /* hold the ref while we probe */ + hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return -EIO; + } + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + hda_pvt = hdac_to_hda_priv(hdev); + if (!hda_pvt) + return -ENOMEM; + + /* ASoC specific initialization */ + ret = snd_soc_register_component(&hdev->dev, + &hdac_hda_codec, hdac_hda_dais, + ARRAY_SIZE(hdac_hda_dais)); + if (ret < 0) { + dev_err(&hdev->dev, "failed to register HDA codec %d\n", ret); + return ret; + } + + dev_set_drvdata(&hdev->dev, hda_pvt); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + + return ret; +} + +static int hdac_hda_dev_remove(struct hdac_device *hdev) +{ + snd_soc_unregister_component(&hdev->dev); + return 0; +} + +static struct hdac_ext_bus_ops hdac_ops = { + .hdev_attach = hdac_hda_dev_probe, + .hdev_detach = hdac_hda_dev_remove, +}; + +struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void) +{ + return &hdac_ops; +} +EXPORT_SYMBOL_GPL(snd_soc_hdac_hda_get_ops); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Extensions for legacy HDA Drivers"); +MODULE_AUTHOR("Rakesh Ughreja"); diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h new file mode 100644 index 00000000000000..d0e440f546b9ce --- /dev/null +++ b/sound/soc/codecs/hdac_hda.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2015-17 Intel Corporation. + */ + +#ifndef __HDAC_HDA_H__ +#define __HDAC_HDA_H__ + +struct hdac_hda_pcm { + int stream_tag[2]; +}; + +struct hdac_hda_priv { + struct hda_codec codec; + struct hdac_hda_pcm pcm[2]; +}; + +#define hdac_to_hda_priv(_hdac) \ + container_of(_hdac, struct hdac_hda_priv, codec.core) +#define hdac_to_hda_codec(_hdac) container_of(_hdac, struct hda_codec, core) + +struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void); + +#endif /* __HDAC_HDA_H__ */ diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 80bb1268a21d64..4bba5fd2302a1d 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -318,6 +318,7 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH tristate "SKL/KBL/BXT/APL with HDA Codecs" select SND_SOC_HDAC_HDMI + select SND_SOC_HDAC_HDA help This adds support for ASoC machine driver for Intel platforms SKL/KBL/BXT/APL with iDisp, HDA audio codecs. diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 971d737cf44252..bf5a6bed7d76d4 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -37,6 +37,7 @@ #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" #include "../../../pci/hda/hda_codec.h" +#include "../../../soc/codecs/hdac_hda.h" /* * initialize the PCI registers @@ -649,6 +650,24 @@ static void skl_clock_device_unregister(struct skl *skl) platform_device_unregister(skl->clk_dev); } +#define IDISP_INTEL_VENDOR_ID 0x80860000 + +/* + * load the legacy codec driver + */ +static void load_codec_module(struct hda_codec *codec) +{ +#ifdef MODULE + char modalias[MODULE_NAME_LEN]; + const char *mod = NULL; + + snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); + mod = modalias; + dev_dbg(&codec->core.dev, "loading %s codec module\n", mod); + request_module(mod); +#endif +} + /* * Probe the given codec address */ @@ -658,7 +677,9 @@ static int probe_codec(struct hdac_bus *bus, int addr) (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res = -1; struct skl *skl = bus_to_skl(bus); + struct hdac_hda_priv *hda_codec; struct hdac_device *hdev; + int err; mutex_lock(&bus->cmd_mutex); snd_hdac_bus_send_cmd(bus, cmd); @@ -668,11 +689,24 @@ static int probe_codec(struct hdac_bus *bus, int addr) return -EIO; dev_dbg(bus->dev, "codec #%d probed OK: %x\n", addr, res); - hdev = devm_kzalloc(&skl->pci->dev, sizeof(*hdev), GFP_KERNEL); - if (!hdev) + hda_codec = devm_kzalloc(&skl->pci->dev, sizeof(*hda_codec), + GFP_KERNEL); + if (!hda_codec) return -ENOMEM; - return snd_hdac_ext_bus_device_init(bus, addr, hdev); + hda_codec->codec.bus = skl_to_hbus(skl); + hdev = &hda_codec->codec.core; + + err = snd_hdac_ext_bus_device_init(bus, addr, hdev); + if (err < 0) + return err; + + /* use legacy bus only for HDA codecs, idisp uses ext bus */ + if ((res & 0xFFFF0000) != IDISP_INTEL_VENDOR_ID) { + hdev->type = HDA_DEV_LEGACY; + load_codec_module(&hda_codec->codec); + } + return 0; } /* Codec initialization */ @@ -807,6 +841,7 @@ static int skl_create(struct pci_dev *pci, const struct hdac_io_ops *io_ops, struct skl **rskl) { + struct hdac_ext_bus_ops *ext_ops; struct skl *skl; struct hdac_bus *bus; struct hda_bus *hbus; @@ -827,7 +862,11 @@ static int skl_create(struct pci_dev *pci, hbus = skl_to_hbus(skl); bus = skl_to_bus(skl); - snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, NULL); + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDA) + ext_ops = snd_soc_hdac_hda_get_ops(); +#endif + snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, ext_ops); bus->use_posbuf = 1; skl->pci = pci; INIT_WORK(&skl->probe_work, skl_probe_work); From b368a81cb17e728321ebb868848d17a0e9f78eb3 Mon Sep 17 00:00:00 2001 From: Rakesh Ughreja Date: Fri, 20 Jul 2018 20:17:44 +0800 Subject: [PATCH 278/285] ASoC: Intel: Skylake: fix widget handling include DAPM Mux and output widgets into the list. Signed-off-by: Rakesh Ughreja Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/skylake/skl-topology.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 1496aac9d23547..f7bcbe1e0f563a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -107,6 +107,9 @@ static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w, case snd_soc_dapm_aif_out: case snd_soc_dapm_dai_out: case snd_soc_dapm_switch: + case snd_soc_dapm_output: + case snd_soc_dapm_mux: + return false; default: return true; From d20de1264ced6d6d801608c556b9eb1677da170b Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 20 Jul 2018 20:17:45 +0800 Subject: [PATCH 279/285] ASoC: Intel: Kconfig: make HDA_DSP_GENERIC_MACH possible for SOF Signed-off-by: Keyon Jie --- sound/soc/intel/boards/Kconfig | 21 +++++++++++---------- sound/soc/sof/intel/Kconfig | 4 ++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 4bba5fd2302a1d..96fe5eeead4dd5 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -270,6 +270,17 @@ config SND_SOC_INTEL_BXT_TDF8532_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH + tristate "SKL/KBL/BXT/APL with HDA Codecs" + select SND_SOC_HDAC_HDMI + select SND_SOC_HDAC_HDA + select SND_SOC_SOF_HDA if SND_SOC_SOF_HDA_COMMON + help + This adds support for ASoC machine driver for Intel platforms + SKL/KBL/BXT/APL with iDisp, HDA audio codecs. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_APOLLOLAKE if SND_SOC_INTEL_SKYLAKE @@ -315,16 +326,6 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH create an alsa sound card for DA7219 + MAX98357A I2S audio codec. Say Y if you have such a device. -config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH - tristate "SKL/KBL/BXT/APL with HDA Codecs" - select SND_SOC_HDAC_HDMI - select SND_SOC_HDAC_HDA - help - This adds support for ASoC machine driver for Intel platforms - SKL/KBL/BXT/APL with iDisp, HDA audio codecs. - Say Y or m if you have such a device. This is a recommended option. - If unsure select "N". - endif ## SND_SOC_INTEL_SKYLAKE if SND_SOC_INTEL_SKYLAKE || SND_SOC_SOF_CANNONLAKE diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index d9b4b6a23f6021..959a60d0ee4c7c 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -92,4 +92,8 @@ config SND_SOC_SOF_HDA_COMMON select SND_SOC_SOF_PCI select SND_SOC_ACPI_INTEL_MATCH +config SND_SOC_SOF_HDA + tristate + depends on SND_SOC_SOF_HDA_COMMON + endif ## SND_SOC_SOF_INTEL From c40438486ca616e7ec1375cfbdd9fbd8cf4f2c61 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 20 Jul 2018 20:17:46 +0800 Subject: [PATCH 280/285] ASoC: SOF: add hda_bus to snd_sof_dev struct to handle hda with sof Signed-off-by: Keyon Jie --- sound/soc/sof/sof-priv.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 5def46b92f1957..aeeeab47913f5e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -24,6 +24,7 @@ #include #include #include +#include "../../pci/hda/hda_codec.h" /* debug flags */ #define SOF_DBG_REGS BIT(1) @@ -308,6 +309,9 @@ struct snd_sof_dev { int ipc_irq; u32 next_comp_id; /* monotonic - reset during S3 */ + /* hda bus */ + struct hda_bus *hbus; + /* memory bases for mmaped DSPs - set by dsp_init() */ void __iomem *bar[SND_SOF_BARS]; /* DSP base address */ int mmio_bar; From 82b3d204182996e657fd5b7ec796ef8e91e31e5a Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 20 Jul 2018 20:17:47 +0800 Subject: [PATCH 281/285] ASoC: SOF: hda-stream: add CORB/RIRB ringbuffers init for HDA. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda-stream.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index a9e81a505aa6c6..2661497381233d 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -525,6 +525,16 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) return -ENOMEM; } + if (sdev->hbus) { + /* mem alloc for the CORB/RIRB ringbuffers */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + PAGE_SIZE, &sdev->hbus->core.rb); + if (ret < 0) { + dev_err(sdev->dev, "error: RB alloc failed\n"); + return -ENOMEM; + } + } + /* create capture streams */ for (i = 0; i < num_capture; i++) { stream = &hdev->cstream[i]; From 7d69f41b737544fb7fd8bc572f8773cac68b1502 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 20 Jul 2018 20:17:48 +0800 Subject: [PATCH 282/285] ASoC: SOF: hda: add hda probing during initialization. Signed-off-by: Keyon Jie --- sound/soc/sof/intel/hda.c | 449 +++++++++++++++++++++++++++++++------- 1 file changed, 367 insertions(+), 82 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 4d6b42028d1ed7..2df932f5f6d959 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -30,6 +30,16 @@ #include #include +/* platform specific devices */ +#include "shim.h" + +#include +#include +#include + +#include "../../../pci/hda/hda_codec.h" +#include "../../codecs/hdac_hda.h" + #include "../sof-priv.h" #include "../ops.h" #include "hda.h" @@ -297,6 +307,268 @@ static const struct sof_intel_dsp_desc *get_chip_info(int pci_id) return NULL; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +static void sof_enable_miscbdcge(struct snd_sof_dev *sdev, bool enable) +{ + u32 val; + + val = enable ? PCI_CGCTL_MISCBDCGE_MASK : 0; + + snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_MISCBDCGE_MASK, val); +} + +/* + * While performing reset, controller may not come back properly causing + * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset + * (init chip) and then again set CGCTL.MISCBDCGE to 1 + */ +static int sof_hda_init_chip(struct snd_sof_dev *sdev, bool full_reset) +{ + struct hdac_bus *bus = &sdev->hbus->core; + int ret; + + sof_enable_miscbdcge(sdev, false); + ret = snd_hdac_bus_init_chip(bus, full_reset); + sof_enable_miscbdcge(sdev, true); + + return ret; +} + +irqreturn_t sof_hda_stream_interrupt(int irq, void *context) +{ + struct hdac_bus *bus = context; + u32 status; + + if (!pm_runtime_active(bus->dev)) + return IRQ_NONE; + + spin_lock(&bus->reg_lock); + + status = snd_hdac_chip_readl(bus, INTSTS); + if (status == 0 || status == 0xffffffff) { + spin_unlock(&bus->reg_lock); + return IRQ_NONE; + } + + /* clear rirb int */ + status = snd_hdac_chip_readb(bus, RIRBSTS); + if (status & RIRB_INT_MASK) { + if (status & RIRB_INT_RESPONSE) + snd_hdac_bus_update_rirb(bus); + snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); + } + + spin_unlock(&bus->reg_lock); + + return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + +/* called from IRQ */ +static void sof_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) +{ + snd_pcm_period_elapsed(hstr->substream); +} + +irqreturn_t sof_hda_stream_threaded_handler(int irq, void *context) +{ + struct hdac_bus *bus = context; + u32 status; + + status = snd_hdac_chip_readl(bus, INTSTS); + + snd_hdac_bus_handle_stream_irq(bus, status, sof_stream_update); + + return IRQ_HANDLED; +} + +static int sof_hda_acquire_irq(struct hda_bus *hbus, int do_disconnect) +{ + int ret; + + /* register our IRQ */ + ret = request_threaded_irq(hbus->pci->irq, sof_hda_stream_interrupt, + sof_hda_stream_threaded_handler, + IRQF_SHARED, "AudioHDA", &hbus->core); + + if (ret) { + dev_err(hbus->core.dev, + "unable to grab IRQ %d, disabling device\n", + hbus->pci->irq); + return ret; + } + + hbus->core.irq = hbus->pci->irq; + pci_intx(hbus->pci, 1); + + return 0; +} + +static int sof_hdac_first_init(struct snd_sof_dev *sdev) +{ + struct hda_bus *hbus = sdev->hbus; + struct hdac_bus *bus = &hbus->core; + struct pci_dev *pci = hbus->pci; + int err; + unsigned short gcap; + + bus->addr = pci_resource_start(pci, 0); + bus->remap_addr = pci_ioremap_bar(pci, 0); + if (bus->remap_addr == NULL) { + dev_err(bus->dev, "ioremap error\n"); + return -ENXIO; + } + + sof_hda_init_chip(sdev, true); + + snd_hdac_bus_parse_capabilities(bus); + + if (sof_hda_acquire_irq(hbus, 0) < 0) + return -EBUSY; + + /* update BARs for sof, don't need parse them again */ + sdev->bar[HDA_DSP_HDA_BAR] = bus->remap_addr; + sdev->bar[HDA_DSP_PP_BAR] = bus->ppcap; + sdev->bar[HDA_DSP_SPIB_BAR] = bus->spbcap; + sdev->bar[HDA_DSP_DRSM_BAR] = bus->drsmcap; + + return 0; +} + +#define IDISP_INTEL_VENDOR_ID 0x80860000 + +/* + * load the legacy codec driver + */ +static void sof_load_codec_module(struct hda_codec *codec) +{ +#ifdef MODULE + char modalias[MODULE_NAME_LEN]; + const char *mod = NULL; + + snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); + mod = modalias; + dev_dbg(&codec->core.dev, "loading %s codec module\n", mod); + request_module(mod); +#endif +} + +/* + * Probe the given codec address + */ +static int sof_probe_hda_codec(struct hda_bus *hbus, int addr) +{ + unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | + (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; + unsigned int res = -1; + struct hdac_hda_priv *hda_codec; + struct hdac_device *hdev; + int err; + + mutex_lock(&hbus->core.cmd_mutex); + snd_hdac_bus_send_cmd(&hbus->core, cmd); + snd_hdac_bus_get_response(&hbus->core, addr, &res); + mutex_unlock(&hbus->core.cmd_mutex); + if (res == -1) + return -EIO; + dev_dbg(hbus->core.dev, "codec #%d probed OK: %x\n", addr, res); + + hda_codec = devm_kzalloc(&hbus->pci->dev, sizeof(*hda_codec), + GFP_KERNEL); + if (!hda_codec) + return -ENOMEM; + + hda_codec->codec.bus = hbus; + hdev = &hda_codec->codec.core; + + err = snd_hdac_ext_bus_device_init(&hbus->core, addr, hdev); + if (err < 0) + return err; + + /* use legacy bus only for HDA codecs, idisp uses ext bus */ + if ((res & 0xFFFF0000) != IDISP_INTEL_VENDOR_ID) { + hdev->type = HDA_DEV_LEGACY; + sof_load_codec_module(&hda_codec->codec); + } + return 0; +} + +/* Codec initialization */ +static void sof_hda_codec_create(struct snd_sof_dev *sdev) +{ + struct hda_bus *hbus = sdev->hbus; + struct hdac_bus *bus = &hbus->core; + int c, max_slots; + + max_slots = HDA_MAX_CODECS; + + /* First try to probe all given codec slots */ + for (c = 0; c < max_slots; c++) { + if ((bus->codec_mask & (1 << c))) { + if (sof_probe_hda_codec(hbus, c) < 0) { + /* + * Some BIOSen give you wrong codec addresses + * that don't exist + */ + dev_warn(bus->dev, + "Codec #%d probe error; disabling it...\n", c); + bus->codec_mask &= ~(1 << c); + /* + * More badly, accessing to a non-existing + * codec often screws up the controller bus, + * and disturbs the further communications. + * Thus if an error occurs during probing, + * better to reset the controller bus to get + * back to the sanity state. + */ + snd_hdac_bus_stop_chip(bus); + sof_hda_init_chip(sdev, true); + } + } + } +} + +static int sof_i915_init(struct hdac_bus *bus) +{ + int err; + + /* + * The HDMI codec is in GPU so we need to ensure that it is powered + * up and ready for probe + */ + err = snd_hdac_i915_init(bus); + if (err < 0) + return err; + + err = snd_hdac_display_power(bus, true); + if (err < 0) + dev_err(bus->dev, "Cannot turn on display power on i915\n"); + + return err; +} + +#else +static int sof_first_init(struct snd_sof_dev *sdev) +{ + struct pci_dev *pci = sdev->pci; + + /* HDA base */ + sdev->bar[HDA_DSP_HDA_BAR] = pci_ioremap_bar(pci, HDA_DSP_HDA_BAR); + if (!sdev->bar[HDA_DSP_HDA_BAR]) { + dev_err(&pci->dev, "error: ioremap error\n"); + /* + * FIXME: why do we return directly, + * should we have a goto err here? + * or should all these gotos be replaced + * by a return? + */ + return -ENXIO; + } + + /* get controller capabilities */ + return hda_dsp_ctrl_get_caps(sdev); +} +#endif + /* * We don't need to do a full HDA codec probe as external HDA codec mode is * considered legacy and will not be supported under SOF. HDMI/DP HDA will @@ -307,6 +579,12 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) struct pci_dev *pci = sdev->pci; struct sof_intel_hda_dev *hdev; struct sof_intel_hda_stream *stream; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + struct hda_bus *hbus; + struct hdac_bus *bus; + struct hdac_ext_bus_ops *ext_ops; + struct hdac_ext_link *hlink = NULL; +#endif const struct sof_intel_dsp_desc *chip; int i; int ret = 0; @@ -318,8 +596,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) if (!chip) { dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); - ret = -EIO; - goto err; + return -EIO; } hdev = devm_kzalloc(&pci->dev, sizeof(*hdev), GFP_KERNEL); @@ -328,27 +605,44 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) sdev->hda = hdev; hdev->desc = chip; - /* HDA base */ - sdev->bar[HDA_DSP_HDA_BAR] = pci_ioremap_bar(pci, HDA_DSP_HDA_BAR); - if (!sdev->bar[HDA_DSP_HDA_BAR]) { - dev_err(&pci->dev, "error: ioremap error\n"); - /* - * FIXME: why do we return directly, - * should we have a goto err here? - * or should all these gotos be replaced - * by a return? - */ - return -ENXIO; - } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + hbus = devm_kzalloc(&pci->dev, sizeof(*hbus), GFP_KERNEL); + if (!hbus) + return -ENOMEM; + + sdev->hbus = hbus; + bus = &hbus->core; + + /* HDA bus init */ +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDA) + ext_ops = snd_soc_hdac_hda_get_ops(); +#endif + snd_hdac_ext_bus_init(bus, &pci->dev, NULL, NULL, ext_ops); + bus->use_posbuf = 1; + bus->bdl_pos_adj = 0; + + mutex_init(&hbus->prepare_mutex); + hbus->pci = pci; + hbus->mixer_assigned = -1; + hbus->modelname = "sofbus"; +#endif + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + /* initialise hdac bus */ + ret = sof_hdac_first_init(sdev); +#else + /* initialise resources for non-HDA */ + ret = sof_first_init(sdev); +#endif + if (ret < 0) + return ret; /* DSP base */ sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR); if (!sdev->bar[HDA_DSP_BAR]) { dev_err(&pci->dev, "error: ioremap error\n"); - ret = -ENXIO; - goto err; + return -ENXIO; } - sdev->mmio_bar = HDA_DSP_BAR; sdev->mailbox_bar = HDA_DSP_BAR; @@ -357,21 +651,12 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) /* allow 64bit DMA address if supported by H/W */ if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(64))) { - dev_dbg(&pci->dev, "DMA mask is 64 bit\n"); dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(64)); } else { - dev_dbg(&pci->dev, "DMA mask is 32 bit\n"); dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)); } - /* get controller capabilities */ - ret = hda_dsp_ctrl_get_caps(sdev); - if (ret < 0) { - dev_err(&pci->dev, "error: failed to find DSP capability\n"); - goto err; - } - /* init streams */ ret = hda_dsp_stream_init(sdev); if (ret < 0) { @@ -380,35 +665,69 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) * not all errors are due to memory issues, but trying * to free everything does not harm */ - goto stream_err; + return ret; } - /* - * clear bits 0-2 of PCI register TCSEL (at offset 0x44) - * TCSEL == Traffic Class Select Register, which sets PCI express QOS - * Ensuring these bits are 0 clears playback static on some HD Audio - * codecs. PCI register TCSEL is defined in the Intel manuals. - */ + /* initialize chip */ snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + sof_hda_init_chip(sdev, true); + + device_disable_async_suspend(bus->dev); + + /* check if dsp is there */ + if (bus->ppcap) + dev_dbg(&pci->dev, "PP capbility, will probe DSP later.\n"); + + if (bus->mlcap) + snd_hdac_ext_bus_get_ml_capabilities(bus); + + snd_hdac_bus_stop_chip(bus); + + + /* probe i915 and HDA codecs */ + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = sof_i915_init(bus); + if (ret < 0) + goto free_streams; + } + + ret = sof_hda_init_chip(sdev, true); + if (ret < 0) { + dev_err(bus->dev, "Init chip failed with ret: %d\n", ret); + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, false); + goto free_streams; + } + + /* codec detection */ + if (!bus->codec_mask) + dev_info(bus->dev, "no hda codecs found!\n"); + + /* create codec instances */ + sof_hda_codec_create(sdev); + + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = snd_hdac_display_power(bus, false); + if (ret < 0) { + dev_err(bus->dev, "Cannot turn off display power on i915\n"); + goto free_streams; + } + } + /* - * while performing reset, controller may not come back properly causing - * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do - * reset (init chip) and then again set CGCTL.MISCBDCGE to 1 + * we are done probing so decrement link counts */ - snd_sof_pci_update_bits(sdev, PCI_CGCTL, - PCI_CGCTL_MISCBDCGE_MASK, 0); - - /* clear WAKESTS */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, - SOF_HDA_WAKESTS_INT_MASK, - SOF_HDA_WAKESTS_INT_MASK); + list_for_each_entry(hlink, &bus->hlink_list, list) + snd_hdac_ext_bus_link_put(bus, hlink); +#endif /* reset HDA controller */ ret = hda_dsp_ctrl_link_reset(sdev); if (ret < 0) { dev_err(&pci->dev, "error: failed to reset HDA controller\n"); - goto stream_err; + goto free_streams; } /* clear stream status */ @@ -446,34 +765,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); - /* - * register our IRQ - * let's try to enable msi firstly - * if it fails, use legacy interrupt mode - * TODO: support interrupt mode selection with kernel parameter - * support msi multiple vectors - */ - ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI); - if (ret < 0) { - dev_info(sdev->dev, "use legacy interrupt mode\n"); - sdev->hda->irq = pci->irq; - sdev->ipc_irq = pci->irq; - } else { - dev_info(sdev->dev, "use msi interrupt mode\n"); - sdev->hda->irq = pci_irq_vector(pci, 0); - /* ipc irq number is the same of hda irq */ - sdev->ipc_irq = sdev->hda->irq; - } - - dev_dbg(sdev->dev, "using HDA IRQ %d\n", sdev->hda->irq); - ret = request_threaded_irq(sdev->hda->irq, hda_dsp_stream_interrupt, - hda_dsp_stream_threaded_handler, - IRQF_SHARED, "AudioHDA", sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n", - sdev->hda->irq); - goto stream_err; - } + sdev->ipc_irq = pci->irq; dev_dbg(sdev->dev, "using IPC IRQ %d\n", sdev->ipc_irq); ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_ipc_irq_handler, @@ -482,7 +774,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to register IPC IRQ %d\n", sdev->ipc_irq); - goto irq_err; + goto free_streams; } /* re-enable CGCTL.MISCBDCGE after reset */ @@ -508,15 +800,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) return 0; -irq_err: - free_irq(sdev->hda->irq, sdev); -stream_err: - pci_free_irq_vectors(pci); +free_streams: hda_dsp_stream_free(sdev); -err: - /* disable DSP */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - SOF_HDA_PPCTL_GPROCEN, 0); + return ret; } @@ -542,7 +828,6 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) SOF_HDA_PPCTL_GPROCEN, 0); free_irq(sdev->ipc_irq, sdev); - free_irq(sdev->pci->irq, sdev); pci_free_irq_vectors(pci); hda_dsp_stream_free(sdev); From 352ca49ad89880b49fcdab01a80c693411477f5b Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 20 Jul 2018 20:17:49 +0800 Subject: [PATCH 283/285] ASoC: Intel: bxt-tdf8532: add iDisp BE dai_links. Signed-off-by: Keyon Jie --- sound/soc/intel/boards/bxt_tdf8532.c | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 336205309e2c8b..ebb8bfe19b9d2f 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -205,6 +205,38 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .dpcm_playback = 1, .no_pcm = 1, }, + { + .name = "iDisp1", + .id = 6, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", +// .codec_name = "i2c-INT34C3:00", +// .codec_dai_name = "tdf8532-hifi", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 7, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 8, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .no_pcm = 1, + }, }; #if !IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL) From b6c5d157bdc90b4df0bc29a6f83fa3d5bd1536b0 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 20 Jul 2018 22:22:45 +0100 Subject: [PATCH 284/285] ASoC: SOF: HDAC: Consolidated code and removed duplication. Moved code by function into correct files. Removed dupliction in stream IRQ handling. Cleaned up init. Many FIXMEs remain. The biggest is removing the old HDA struct in sdev. Signed-off-by: Liam Girdwood --- sound/soc/sof/intel/Makefile | 3 +- sound/soc/sof/intel/hda-codec.c | 134 +++++++++ sound/soc/sof/intel/hda-ctrl.c | 24 ++ sound/soc/sof/intel/hda-stream.c | 27 +- sound/soc/sof/intel/hda.c | 471 ++++++++++++------------------- sound/soc/sof/intel/hda.h | 9 + sound/soc/sof/sof-priv.h | 6 +- 7 files changed, 372 insertions(+), 302 deletions(-) create mode 100644 sound/soc/sof/intel/hda-codec.c diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 7b5784d88fd154..ae2b7778200ab5 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -6,7 +6,8 @@ snd-sof-intel-byt-objs := byt.o snd-sof-intel-hsw-objs := hsw.o snd-sof-intel-bdw-objs := bdw.o snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ - hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o hda-dai.o\ + hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ + hda-dai.o hda-codec.o \ skl.o apl.o cnl.o obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-intel-byt.o diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c new file mode 100644 index 00000000000000..83a544b974aa98 --- /dev/null +++ b/sound/soc/sof/intel/hda-codec.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Authors: Jeeja KP + * Keyon Jie + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../pci/hda/hda_codec.h" +#include "../../codecs/hdac_hda.h" + +#include "../sof-priv.h" +#include "../ops.h" +#include "hda.h" + +#define IDISP_INTEL_VENDOR_ID 0x80860000 + +/* load the legacy codec driver */ +#ifdef MODULE +static void hda_codec_load_module(struct hda_codec *codec) +{ + char modalias[MODULE_NAME_LEN]; + const char *mod = modalias; + + snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); + dev_dbg(&codec->core.dev, "loading %s codec module\n", mod); + request_module(mod); +} +#else +static void hda_codec_load_module(struct hda_codec *codec) {} +#endif + +/* probe individual codec */ +static int hda_codec_probe(struct snd_sof_dev *sdev, int addr) +{ + struct hda_bus *hbus = sdev->hbus; + unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | + (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; + unsigned int res = -1; + struct hdac_hda_priv *hda_codec; + struct hdac_device *hdev; + int err; + + /* make sure codec responds at this address */ + mutex_lock(&hbus->core.cmd_mutex); + snd_hdac_bus_send_cmd(&hbus->core, cmd); + snd_hdac_bus_get_response(&hbus->core, addr, &res); + mutex_unlock(&hbus->core.cmd_mutex); + if (res == -1) + return -EIO; + dev_dbg(sdev->dev, "codec #%d probed OK: %x\n", addr, res); + + hda_codec = devm_kzalloc(&hbus->pci->dev, sizeof(*hda_codec), + GFP_KERNEL); + if (!hda_codec) + return -ENOMEM; + + hda_codec->codec.bus = hbus; + hdev = &hda_codec->codec.core; + + err = snd_hdac_ext_bus_device_init(&hbus->core, addr, hdev); + if (err < 0) + return err; + + /* use legacy bus only for HDA codecs, idisp uses ext bus */ + if ((res & 0xFFFF0000) != IDISP_INTEL_VENDOR_ID) { + hdev->type = HDA_DEV_LEGACY; + hda_codec_load_module(&hda_codec->codec); + } + + return 0; +} + +/* Codec initialization */ +int hda_codec_probe_bus(struct snd_sof_dev *sdev) +{ + struct hda_bus *hbus = sdev->hbus; + struct hdac_bus *bus = &hbus->core; + int c, max_slots, ret = 0; + + max_slots = HDA_MAX_CODECS; + + /* probe codecs in avail slots */ + for (c = 0; c < max_slots; c++) { + + if (!(bus->codec_mask & (1 << c))) + continue; + + ret = hda_codec_probe(sdev, c); + if (ret < 0) { + dev_err(bus->dev,"codec #%d probe error %d\n", c, ret); + return ret; + } + } + + return 0; +} + +int hda_codec_i915_init(struct snd_sof_dev *sdev) +{ + struct hda_bus *hbus = sdev->hbus; + struct hdac_bus *bus = &hbus->core; + int ret; + + /* i915 exposes a HDA codec for HDMI audio */ + ret = snd_hdac_i915_init(bus); + if (ret < 0) + return ret; + + ret = snd_hdac_display_power(bus, true); + if (ret < 0) + dev_err(bus->dev, "i915 HDAC power on failed %d\n", ret); + + return ret; +} + diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index 57b877bde8a8f4..eac0815de78734 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -131,3 +131,27 @@ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) return ret; } +void hda_dsp_ctrl_enable_miscbdcge(struct snd_sof_dev *sdev, bool enable) +{ + u32 val = enable ? PCI_CGCTL_MISCBDCGE_MASK : 0; + + snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_MISCBDCGE_MASK, val); +} + +/* + * While performing reset, controller may not come back properly causing + * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset + * (init chip) and then again set CGCTL.MISCBDCGE to 1 + */ +int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) +{ + struct hdac_bus *bus = &sdev->hbus->core; + int ret; + + hda_dsp_ctrl_enable_miscbdcge(sdev, false); + ret = snd_hdac_bus_init_chip(bus, full_reset); + hda_dsp_ctrl_enable_miscbdcge(sdev, true); + + return ret; +} + diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 2661497381233d..d753a83c746b72 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -407,17 +408,33 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) { struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; + struct hdac_bus *bus = &sdev->hbus->core; u32 status; if (!pm_runtime_active(sdev->dev)) return IRQ_NONE; - status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + spin_lock(&bus->reg_lock); - if (status == 0 || status == 0xffffffff) + status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + if (status == 0 || status == 0xffffffff) { + spin_unlock(&bus->reg_lock); return IRQ_NONE; + } - return status ? IRQ_WAKE_THREAD : IRQ_HANDLED; + /* clear rirb int */ + status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_RIRBSTS); + if (status & RIRB_INT_MASK) { + if (status & RIRB_INT_RESPONSE) + snd_hdac_bus_update_rirb(bus); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_RIRBSTS, + RIRB_INT_MASK); + } + + spin_unlock(&bus->reg_lock); + + return snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS) + ? IRQ_WAKE_THREAD : IRQ_HANDLED; } irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) @@ -480,6 +497,10 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) } } + // TODO: legacy code call snd_pcm_period_elapsed(hstr->substream); + // we probably dont need this since we get updates via IPC/SRAM/ + // TODO: evaluate. + return IRQ_HANDLED; } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 2df932f5f6d959..a65dca8c446326 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -29,20 +29,19 @@ #include #include #include - -/* platform specific devices */ -#include "shim.h" - -#include #include #include -#include "../../../pci/hda/hda_codec.h" -#include "../../codecs/hdac_hda.h" - #include "../sof-priv.h" #include "../ops.h" #include "hda.h" +#include "../../../pci/hda/hda_codec.h" +#include "../../codecs/hdac_hda.h" + +/* platform specific devices */ +#include "shim.h" + + /* * Register IO @@ -308,79 +307,8 @@ static const struct sof_intel_dsp_desc *get_chip_info(int pci_id) } #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) -static void sof_enable_miscbdcge(struct snd_sof_dev *sdev, bool enable) -{ - u32 val; - - val = enable ? PCI_CGCTL_MISCBDCGE_MASK : 0; - - snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_MISCBDCGE_MASK, val); -} - -/* - * While performing reset, controller may not come back properly causing - * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset - * (init chip) and then again set CGCTL.MISCBDCGE to 1 - */ -static int sof_hda_init_chip(struct snd_sof_dev *sdev, bool full_reset) -{ - struct hdac_bus *bus = &sdev->hbus->core; - int ret; - - sof_enable_miscbdcge(sdev, false); - ret = snd_hdac_bus_init_chip(bus, full_reset); - sof_enable_miscbdcge(sdev, true); - - return ret; -} - -irqreturn_t sof_hda_stream_interrupt(int irq, void *context) -{ - struct hdac_bus *bus = context; - u32 status; - - if (!pm_runtime_active(bus->dev)) - return IRQ_NONE; - - spin_lock(&bus->reg_lock); - - status = snd_hdac_chip_readl(bus, INTSTS); - if (status == 0 || status == 0xffffffff) { - spin_unlock(&bus->reg_lock); - return IRQ_NONE; - } - - /* clear rirb int */ - status = snd_hdac_chip_readb(bus, RIRBSTS); - if (status & RIRB_INT_MASK) { - if (status & RIRB_INT_RESPONSE) - snd_hdac_bus_update_rirb(bus); - snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); - } - - spin_unlock(&bus->reg_lock); - - return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED; -} - -/* called from IRQ */ -static void sof_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) -{ - snd_pcm_period_elapsed(hstr->substream); -} - -irqreturn_t sof_hda_stream_threaded_handler(int irq, void *context) -{ - struct hdac_bus *bus = context; - u32 status; - - status = snd_hdac_chip_readl(bus, INTSTS); - - snd_hdac_bus_handle_stream_irq(bus, status, sof_stream_update); - - return IRQ_HANDLED; -} +#if 0 static int sof_hda_acquire_irq(struct hda_bus *hbus, int do_disconnect) { int ret; @@ -388,7 +316,7 @@ static int sof_hda_acquire_irq(struct hda_bus *hbus, int do_disconnect) /* register our IRQ */ ret = request_threaded_irq(hbus->pci->irq, sof_hda_stream_interrupt, sof_hda_stream_threaded_handler, - IRQF_SHARED, "AudioHDA", &hbus->core); + IRQF_SHARED, "SOFHDA", &hbus->core); if (ret) { dev_err(hbus->core.dev, @@ -402,15 +330,39 @@ static int sof_hda_acquire_irq(struct hda_bus *hbus, int do_disconnect) return 0; } +#endif -static int sof_hdac_first_init(struct snd_sof_dev *sdev) +static int hda_init(struct snd_sof_dev *sdev) { - struct hda_bus *hbus = sdev->hbus; - struct hdac_bus *bus = &hbus->core; - struct pci_dev *pci = hbus->pci; - int err; - unsigned short gcap; + struct hda_bus *hbus; + struct hdac_bus *bus; + struct hdac_ext_bus_ops *ext_ops; + struct pci_dev *pci = sdev->pci; + //struct hdac_ext_link *hlink = NULL; + //int err; + //unsigned short gcap; + + hbus = devm_kzalloc(&pci->dev, sizeof(*hbus), GFP_KERNEL); + if (!hbus) + return -ENOMEM; + + sdev->hbus = hbus; + bus = &hbus->core; + /* HDA bus init */ +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDA) + ext_ops = snd_soc_hdac_hda_get_ops(); +#endif + snd_hdac_ext_bus_init(bus, &pci->dev, NULL, NULL, ext_ops); + bus->use_posbuf = 1; + bus->bdl_pos_adj = 0; + + mutex_init(&hbus->prepare_mutex); + hbus->pci = pci; + hbus->mixer_assigned = -1; + hbus->modelname = "sofbus"; + + /* initialise hdac bus */ bus->addr = pci_resource_start(pci, 0); bus->remap_addr = pci_ioremap_bar(pci, 0); if (bus->remap_addr == NULL) { @@ -418,12 +370,13 @@ static int sof_hdac_first_init(struct snd_sof_dev *sdev) return -ENXIO; } - sof_hda_init_chip(sdev, true); + // FIXME: we do this alot ! + hda_dsp_ctrl_init_chip(sdev, true); snd_hdac_bus_parse_capabilities(bus); - if (sof_hda_acquire_irq(hbus, 0) < 0) - return -EBUSY; + //if (sof_hda_acquire_irq(hbus, 0) < 0) + // return -EBUSY; /* update BARs for sof, don't need parse them again */ sdev->bar[HDA_DSP_HDA_BAR] = bus->remap_addr; @@ -434,139 +387,107 @@ static int sof_hdac_first_init(struct snd_sof_dev *sdev) return 0; } -#define IDISP_INTEL_VENDOR_ID 0x80860000 - -/* - * load the legacy codec driver - */ -static void sof_load_codec_module(struct hda_codec *codec) +static int hda_init_caps(struct snd_sof_dev *sdev) { -#ifdef MODULE - char modalias[MODULE_NAME_LEN]; - const char *mod = NULL; - - snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); - mod = modalias; - dev_dbg(&codec->core.dev, "loading %s codec module\n", mod); - request_module(mod); -#endif -} + struct hdac_bus *bus = &sdev->hbus->core; + struct pci_dev *pci = sdev->pci; + struct hdac_ext_link *hlink = NULL; + int ret = 0; -/* - * Probe the given codec address - */ -static int sof_probe_hda_codec(struct hda_bus *hbus, int addr) -{ - unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | - (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; - unsigned int res = -1; - struct hdac_hda_priv *hda_codec; - struct hdac_device *hdev; - int err; - - mutex_lock(&hbus->core.cmd_mutex); - snd_hdac_bus_send_cmd(&hbus->core, cmd); - snd_hdac_bus_get_response(&hbus->core, addr, &res); - mutex_unlock(&hbus->core.cmd_mutex); - if (res == -1) - return -EIO; - dev_dbg(hbus->core.dev, "codec #%d probed OK: %x\n", addr, res); - - hda_codec = devm_kzalloc(&hbus->pci->dev, sizeof(*hda_codec), - GFP_KERNEL); - if (!hda_codec) - return -ENOMEM; + // FIXME: we do this alot ! + hda_dsp_ctrl_init_chip(sdev, true); + + device_disable_async_suspend(bus->dev); + + /* check if dsp is there */ + if (bus->ppcap) + dev_dbg(&pci->dev, "PP capbility, will probe DSP later.\n"); - hda_codec->codec.bus = hbus; - hdev = &hda_codec->codec.core; + if (bus->mlcap) + snd_hdac_ext_bus_get_ml_capabilities(bus); - err = snd_hdac_ext_bus_device_init(&hbus->core, addr, hdev); - if (err < 0) - return err; + snd_hdac_bus_stop_chip(bus); - /* use legacy bus only for HDA codecs, idisp uses ext bus */ - if ((res & 0xFFFF0000) != IDISP_INTEL_VENDOR_ID) { - hdev->type = HDA_DEV_LEGACY; - sof_load_codec_module(&hda_codec->codec); + /* probe i915 and HDA codecs */ + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = hda_codec_i915_init(sdev); + if (ret < 0) + return ret; } - return 0; -} -/* Codec initialization */ -static void sof_hda_codec_create(struct snd_sof_dev *sdev) -{ - struct hda_bus *hbus = sdev->hbus; - struct hdac_bus *bus = &hbus->core; - int c, max_slots; - - max_slots = HDA_MAX_CODECS; - - /* First try to probe all given codec slots */ - for (c = 0; c < max_slots; c++) { - if ((bus->codec_mask & (1 << c))) { - if (sof_probe_hda_codec(hbus, c) < 0) { - /* - * Some BIOSen give you wrong codec addresses - * that don't exist - */ - dev_warn(bus->dev, - "Codec #%d probe error; disabling it...\n", c); - bus->codec_mask &= ~(1 << c); - /* - * More badly, accessing to a non-existing - * codec often screws up the controller bus, - * and disturbs the further communications. - * Thus if an error occurs during probing, - * better to reset the controller bus to get - * back to the sanity state. - */ - snd_hdac_bus_stop_chip(bus); - sof_hda_init_chip(sdev, true); - } - } + // FIXME: we do this alot ! + ret = hda_dsp_ctrl_init_chip(sdev, true); + if (ret < 0) { + dev_err(bus->dev, "Init chip failed with ret: %d\n", ret); + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_display_power(bus, false); + return ret; } -} -static int sof_i915_init(struct hdac_bus *bus) -{ - int err; + /* codec detection */ + if (!bus->codec_mask) + dev_info(bus->dev, "no hda codecs found!\n"); + + /* create codec instances */ + hda_codec_probe_bus(sdev); + + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = snd_hdac_display_power(bus, false); + if (ret < 0) { + dev_err(bus->dev, "Cannot turn off display power on i915\n"); + return ret; + } + } /* - * The HDMI codec is in GPU so we need to ensure that it is powered - * up and ready for probe + * we are done probing so decrement link counts */ - err = snd_hdac_i915_init(bus); - if (err < 0) - return err; - - err = snd_hdac_display_power(bus, true); - if (err < 0) - dev_err(bus->dev, "Cannot turn on display power on i915\n"); + list_for_each_entry(hlink, &bus->hlink_list, list) + snd_hdac_ext_bus_link_put(bus, hlink); - return err; + return 0; } #else -static int sof_first_init(struct snd_sof_dev *sdev) + +static int hda_init(struct snd_sof_dev *sdev) { struct pci_dev *pci = sdev->pci; + int ret; /* HDA base */ sdev->bar[HDA_DSP_HDA_BAR] = pci_ioremap_bar(pci, HDA_DSP_HDA_BAR); if (!sdev->bar[HDA_DSP_HDA_BAR]) { dev_err(&pci->dev, "error: ioremap error\n"); - /* - * FIXME: why do we return directly, - * should we have a goto err here? - * or should all these gotos be replaced - * by a return? - */ return -ENXIO; } /* get controller capabilities */ - return hda_dsp_ctrl_get_caps(sdev); + ret = hda_dsp_ctrl_get_caps(sdev); + if (ret < 0) + dev_err(&pci->dev, "error: get caps error\n"); + + return 0; } + +static int hda_init_caps(struct snd_sof_dev *sdev) +{ + /* + * while performing reset, controller may not come back properly causing + * issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do + * reset (init chip) and then again set CGCTL.MISCBDCGE to 1 + */ + snd_sof_pci_update_bits(sdev, PCI_CGCTL, + PCI_CGCTL_MISCBDCGE_MASK, 0); + + /* clear WAKESTS */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK, + SOF_HDA_WAKESTS_INT_MASK); + + return 0; +} + #endif /* @@ -579,12 +500,6 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) struct pci_dev *pci = sdev->pci; struct sof_intel_hda_dev *hdev; struct sof_intel_hda_stream *stream; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - struct hda_bus *hbus; - struct hdac_bus *bus; - struct hdac_ext_bus_ops *ext_ops; - struct hdac_ext_link *hlink = NULL; -#endif const struct sof_intel_dsp_desc *chip; int i; int ret = 0; @@ -596,7 +511,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) if (!chip) { dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); - return -EIO; + ret = -EIO; + goto err; } hdev = devm_kzalloc(&pci->dev, sizeof(*hdev), GFP_KERNEL); @@ -605,44 +521,19 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) sdev->hda = hdev; hdev->desc = chip; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - hbus = devm_kzalloc(&pci->dev, sizeof(*hbus), GFP_KERNEL); - if (!hbus) - return -ENOMEM; - - sdev->hbus = hbus; - bus = &hbus->core; - - /* HDA bus init */ -#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDA) - ext_ops = snd_soc_hdac_hda_get_ops(); -#endif - snd_hdac_ext_bus_init(bus, &pci->dev, NULL, NULL, ext_ops); - bus->use_posbuf = 1; - bus->bdl_pos_adj = 0; - - mutex_init(&hbus->prepare_mutex); - hbus->pci = pci; - hbus->mixer_assigned = -1; - hbus->modelname = "sofbus"; -#endif - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - /* initialise hdac bus */ - ret = sof_hdac_first_init(sdev); -#else - /* initialise resources for non-HDA */ - ret = sof_first_init(sdev); -#endif + /* set up HDA base */ + ret = hda_init(sdev); if (ret < 0) - return ret; + goto err; /* DSP base */ sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR); if (!sdev->bar[HDA_DSP_BAR]) { dev_err(&pci->dev, "error: ioremap error\n"); - return -ENXIO; + ret = -ENXIO; + goto err; } + sdev->mmio_bar = HDA_DSP_BAR; sdev->mailbox_bar = HDA_DSP_BAR; @@ -651,8 +542,10 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) /* allow 64bit DMA address if supported by H/W */ if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(64))) { + dev_dbg(&pci->dev, "DMA mask is 64 bit\n"); dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(64)); } else { + dev_dbg(&pci->dev, "DMA mask is 32 bit\n"); dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)); } @@ -665,69 +558,27 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) * not all errors are due to memory issues, but trying * to free everything does not harm */ - return ret; - } - - /* initialize chip */ - snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - sof_hda_init_chip(sdev, true); - - device_disable_async_suspend(bus->dev); - - /* check if dsp is there */ - if (bus->ppcap) - dev_dbg(&pci->dev, "PP capbility, will probe DSP later.\n"); - - if (bus->mlcap) - snd_hdac_ext_bus_get_ml_capabilities(bus); - - snd_hdac_bus_stop_chip(bus); - - - /* probe i915 and HDA codecs */ - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { - ret = sof_i915_init(bus); - if (ret < 0) - goto free_streams; - } - - ret = sof_hda_init_chip(sdev, true); - if (ret < 0) { - dev_err(bus->dev, "Init chip failed with ret: %d\n", ret); - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) - snd_hdac_display_power(bus, false); - goto free_streams; - } - - /* codec detection */ - if (!bus->codec_mask) - dev_info(bus->dev, "no hda codecs found!\n"); - - /* create codec instances */ - sof_hda_codec_create(sdev); - - if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { - ret = snd_hdac_display_power(bus, false); - if (ret < 0) { - dev_err(bus->dev, "Cannot turn off display power on i915\n"); - goto free_streams; - } + goto stream_err; } /* - * we are done probing so decrement link counts + * clear bits 0-2 of PCI register TCSEL (at offset 0x44) + * TCSEL == Traffic Class Select Register, which sets PCI express QOS + * Ensuring these bits are 0 clears playback static on some HD Audio + * codecs. PCI register TCSEL is defined in the Intel manuals. */ - list_for_each_entry(hlink, &bus->hlink_list, list) - snd_hdac_ext_bus_link_put(bus, hlink); -#endif + snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); + + /* init HDA capabilities */ + ret = hda_init_caps(sdev); + if (ret < 0) + goto stream_err; /* reset HDA controller */ ret = hda_dsp_ctrl_link_reset(sdev); if (ret < 0) { dev_err(&pci->dev, "error: failed to reset HDA controller\n"); - goto free_streams; + goto stream_err; } /* clear stream status */ @@ -765,7 +616,34 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); - sdev->ipc_irq = pci->irq; + /* + * register our IRQ + * let's try to enable msi firstly + * if it fails, use legacy interrupt mode + * TODO: support interrupt mode selection with kernel parameter + * support msi multiple vectors + */ + ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI); + if (ret < 0) { + dev_info(sdev->dev, "use legacy interrupt mode\n"); + sdev->hda->irq = pci->irq; + sdev->ipc_irq = pci->irq; + } else { + dev_info(sdev->dev, "use msi interrupt mode\n"); + sdev->hda->irq = pci_irq_vector(pci, 0); + /* ipc irq number is the same of hda irq */ + sdev->ipc_irq = sdev->hda->irq; + } + + dev_dbg(sdev->dev, "using HDA IRQ %d\n", sdev->hda->irq); + ret = request_threaded_irq(sdev->hda->irq, hda_dsp_stream_interrupt, + hda_dsp_stream_threaded_handler, + IRQF_SHARED, "AudioHDA", sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n", + sdev->hda->irq); + goto stream_err; + } dev_dbg(sdev->dev, "using IPC IRQ %d\n", sdev->ipc_irq); ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_ipc_irq_handler, @@ -774,13 +652,11 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to register IPC IRQ %d\n", sdev->ipc_irq); - goto free_streams; + goto irq_err; } /* re-enable CGCTL.MISCBDCGE after reset */ - snd_sof_pci_update_bits(sdev, PCI_CGCTL, - PCI_CGCTL_MISCBDCGE_MASK, - PCI_CGCTL_MISCBDCGE_MASK); + hda_dsp_ctrl_enable_miscbdcge(sdev, true); device_disable_async_suspend(&pci->dev); @@ -800,9 +676,15 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) return 0; -free_streams: +irq_err: + free_irq(sdev->hda->irq, sdev); +stream_err: + pci_free_irq_vectors(pci); hda_dsp_stream_free(sdev); - +err: + /* disable DSP */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + SOF_HDA_PPCTL_GPROCEN, 0); return ret; } @@ -828,6 +710,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) SOF_HDA_PPCTL_GPROCEN, 0); free_irq(sdev->ipc_irq, sdev); + free_irq(sdev->pci->irq, sdev); pci_free_irq_vectors(pci); hda_dsp_stream_free(sdev); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 77a2315223a0da..6ff873f66be456 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -28,6 +28,7 @@ #define SOF_HDA_INTSTS 0x24 #define SOF_HDA_WAKESTS 0x0E #define SOF_HDA_WAKESTS_INT_MASK ((1 << 8) - 1) +#define SOF_HDA_RIRBSTS 0x5d /* SOF_HDA_GCTL register bist */ #define SOF_HDA_GCTL_RESET BIT(0) @@ -486,6 +487,14 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); */ int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev); int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev); +void hda_dsp_ctrl_enable_miscbdcge(struct snd_sof_dev *sdev, bool enable); +int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset); + +/* + * HDA Codec operations. + */ +int hda_codec_probe_bus(struct snd_sof_dev *sdev); +int hda_codec_i915_init(struct snd_sof_dev *sdev); /* * Trace Control. diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index aeeeab47913f5e..e1c2d1deb5f667 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -297,7 +297,8 @@ struct snd_sof_dev { /* DSP HW differentiation */ struct snd_sof_pdata *pdata; const struct snd_sof_dsp_ops *ops; - struct sof_intel_hda_dev *hda; /* for HDA based DSP HW */ + struct sof_intel_hda_dev *hda; /* for HDA based DSP HW FIXME: delete this and use hbus instead */ + struct hda_bus *hbus; const struct sof_arch_ops *arch_ops; /* IPC */ @@ -309,9 +310,6 @@ struct snd_sof_dev { int ipc_irq; u32 next_comp_id; /* monotonic - reset during S3 */ - /* hda bus */ - struct hda_bus *hbus; - /* memory bases for mmaped DSPs - set by dsp_init() */ void __iomem *bar[SND_SOF_BARS]; /* DSP base address */ int mmio_bar; From c465f8293449c7919f23aed5979cc91e9c44e27c Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Mon, 23 Jul 2018 01:13:09 -0400 Subject: [PATCH 285/285] ASoC: SOF: use hdac_bus to manage streams This patch doesn't replace struct sof_intel_hda_dev with hda_bus, but embed hda_bus in sof_intel_hda_dev. It's becasue there are a few memebers of sof_intel_hda_dev are not in hda_bus. Host streams are managed by hdac_bus's stream_list now. Compiling okay. But not test yet. May have conflict with Keyon's PR today. I'll rebase and merge it to Keyon's PR later. Signed-off-by: Mengdong Lin --- sound/soc/sof/intel/hda-codec.c | 8 +- sound/soc/sof/intel/hda-ctrl.c | 2 +- sound/soc/sof/intel/hda-loader.c | 14 +- sound/soc/sof/intel/hda-stream.c | 228 +++++++++++++++++-------------- sound/soc/sof/intel/hda.c | 29 +--- sound/soc/sof/intel/hda.h | 17 ++- sound/soc/sof/sof-priv.h | 4 +- 7 files changed, 155 insertions(+), 147 deletions(-) diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 83a544b974aa98..d35ca8a1085128 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -51,7 +51,7 @@ static void hda_codec_load_module(struct hda_codec *codec) {} /* probe individual codec */ static int hda_codec_probe(struct snd_sof_dev *sdev, int addr) { - struct hda_bus *hbus = sdev->hbus; + struct hda_bus *hbus = sof_to_hbus(sdev); unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res = -1; @@ -92,8 +92,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int addr) /* Codec initialization */ int hda_codec_probe_bus(struct snd_sof_dev *sdev) { - struct hda_bus *hbus = sdev->hbus; - struct hdac_bus *bus = &hbus->core; + struct hdac_bus *bus = sof_to_bus(sdev); int c, max_slots, ret = 0; max_slots = HDA_MAX_CODECS; @@ -116,8 +115,7 @@ int hda_codec_probe_bus(struct snd_sof_dev *sdev) int hda_codec_i915_init(struct snd_sof_dev *sdev) { - struct hda_bus *hbus = sdev->hbus; - struct hdac_bus *bus = &hbus->core; + struct hdac_bus *bus = sof_to_bus(sdev); int ret; /* i915 exposes a HDA codec for HDMI audio */ diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index eac0815de78734..a55801a3d93433 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -145,7 +145,7 @@ void hda_dsp_ctrl_enable_miscbdcge(struct snd_sof_dev *sdev, bool enable) */ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) { - struct hdac_bus *bus = &sdev->hbus->core; + struct hdac_bus *bus = sof_to_bus(sdev); int ret; hda_dsp_ctrl_enable_miscbdcge(sdev, false); diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 372ccfaac30647..fbb3943422380e 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -242,17 +242,19 @@ static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, static int cl_copy_fw(struct snd_sof_dev *sdev, int tag) { - struct sof_intel_hda_stream *stream = NULL; - struct sof_intel_hda_dev *hdev = sdev->hda; - int ret, status, i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *s, *stream = NULL; + int ret, status; /* get stream with tag */ - for (i = 0; i < hdev->num_playback; i++) { - if (hdev->pstream[i].tag == tag) { - stream = &hdev->pstream[i]; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_PLAYBACK + && s->tag == tag) { + stream = s; break; } } + if (!stream) { dev_err(sdev->dev, "error: could not get stream with stream tag%d\n", diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index d753a83c746b72..a61a3e20942d14 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -116,19 +116,44 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, return 0; } + +/* get next unused stream */ +struct sof_intel_hda_stream * +hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *s, *stream = NULL; + + /* get an unused playback stream */ + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == direction && !s->open) { + s->open = true; + stream = s; + break; + } + } + + /* stream found ? */ + if (!stream) + dev_err(sdev->dev, "error: no free %s streams\n", + direction ==SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture" ); + + return stream; +} + /* get next unused playback stream */ struct sof_intel_hda_stream * hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev) { - struct sof_intel_hda_dev *hdev = sdev->hda; - struct sof_intel_hda_stream *stream = NULL; - int i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *s, *stream = NULL; /* get an unused playback stream */ - for (i = 0; i < hdev->num_playback; i++) { - if (!hdev->pstream[i].open) { - hdev->pstream[i].open = true; - stream = &hdev->pstream[i]; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_PLAYBACK + && !s->open) { + s->open = true; + stream = s; break; } } @@ -144,15 +169,15 @@ hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev) struct sof_intel_hda_stream * hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev) { - struct sof_intel_hda_dev *hdev = sdev->hda; - struct sof_intel_hda_stream *stream = NULL; - int i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *s, *stream = NULL; /* get an unused capture stream */ - for (i = 0; i < hdev->num_capture; i++) { - if (!hdev->cstream[i].open) { - hdev->cstream[i].open = true; - stream = &hdev->cstream[i]; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_CAPTURE + && !s->open) { + s->open = true; + stream = s; break; } } @@ -164,17 +189,36 @@ hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev) return stream; } +/* free a stream */ +int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int tag) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *s; + + /* find used stream */ + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == direction + && s->open && s->tag == tag) { + s->open = false; + return 0; + } + } + + dev_dbg(sdev->dev, "tag %d not opened!\n", tag); + return -ENODEV; +} + /* free playback stream */ int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int tag) { - struct sof_intel_hda_dev *hdev = sdev->hda; - int i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *s; /* find used playback stream */ - for (i = 0; i < hdev->num_playback; i++) { - if (hdev->pstream[i].open && - hdev->pstream[i].tag == tag) { - hdev->pstream[i].open = false; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_PLAYBACK + && s->open && s->tag == tag) { + s->open = false; return 0; } } @@ -186,14 +230,14 @@ int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int tag) /* free capture stream */ int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int tag) { - struct sof_intel_hda_dev *hdev = sdev->hda; - int i; + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *s; /* find used capture stream */ - for (i = 0; i < hdev->num_capture; i++) { - if (hdev->cstream[i].open && - hdev->cstream[i].tag == tag) { - hdev->cstream[i].open = false; + list_for_each_entry(s, &bus->stream_list, list) { + if (s->direction == SNDRV_PCM_STREAM_CAPTURE + && s->open && s->tag == tag) { + s->open = false; return 0; } } @@ -256,7 +300,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params) { - struct sof_intel_hda_dev *hdev = sdev->hda; + struct hdac_bus *bus = sof_to_bus(sdev); struct sof_intel_dsp_bdl *bdl; int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; u32 val, mask; @@ -382,7 +426,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) & SOF_HDA_ADSP_DPLBASE_ENABLE)) snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, - (u32)hdev->posbuffer.addr | + (u32)bus->posbuf.addr | SOF_HDA_ADSP_DPLBASE_ENABLE); /* set interrupt enable bits */ @@ -408,7 +452,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) { struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; - struct hdac_bus *bus = &sdev->hbus->core; + struct hdac_bus *bus = sof_to_bus(sdev); u32 status; if (!pm_runtime_active(sdev->dev)) @@ -440,63 +484,40 @@ irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) { struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; - struct sof_intel_hda_dev *hdev = sdev->hda; + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *s; + //struct sof_intel_hda_dev *hdev = sdev->hda; u32 status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); u32 sd_status; - int i; - /* check playback streams */ - for (i = 0; i < hdev->num_playback; i++) { - /* is IRQ for this stream ? */ - if (status & (1 << hdev->pstream[i].index)) { + /* check streams */ + list_for_each_entry(s, &bus->stream_list, list) { + if (status & (1 << s->index) + && !s->open) { sd_status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - hdev->pstream[i].sd_offset + + s->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS) & 0xff; - dev_dbg(sdev->dev, "pstream %d status 0x%x\n", - i, sd_status); + dev_dbg(sdev->dev, "stream %d status 0x%x\n", + s->index, sd_status); snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - hdev->pstream[i].sd_offset + + s->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK, SOF_HDA_CL_DMA_SD_INT_MASK); - if (!hdev->pstream[i].substream || - !hdev->pstream[i].running || + if (!s->substream || + !s->running || (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) continue; - } - } - - /* check capture streams */ - for (i = 0; i < hdev->num_capture; i++) { - /* is IRQ for this stream ? */ - if (status & (1 << hdev->cstream[i].index)) { - sd_status = - snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - hdev->cstream[i].sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS) & - 0xff; - - dev_dbg(sdev->dev, "cstream %d status 0x%x\n", - i, sd_status); - - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - hdev->cstream[i].sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_CL_DMA_SD_INT_MASK); - if (!hdev->cstream[i].substream || - !hdev->cstream[i].running || - (sd_status & SOF_HDA_CL_DMA_SD_INT_MASK) == 0) - continue; } } + // TODO: legacy code call snd_pcm_period_elapsed(hstr->substream); // we probably dont need this since we get updates via IPC/SRAM/ // TODO: evaluate. @@ -506,7 +527,7 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) int hda_dsp_stream_init(struct snd_sof_dev *sdev) { - struct sof_intel_hda_dev *hdev = sdev->hda; + struct hdac_bus *bus = sof_to_bus(sdev); struct sof_intel_hda_stream *stream; struct pci_dev *pci = sdev->pci; int i, num_playback, num_capture, num_total, ret; @@ -520,9 +541,6 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) num_playback = (gcap >> 12) & 0x0f; num_total = num_playback + num_capture; - hdev->num_capture = num_capture; - hdev->num_playback = num_playback; - dev_dbg(sdev->dev, "detected %d playback and %d capture streams\n", num_playback, num_capture); @@ -539,26 +557,29 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) } /* mem alloc for the position buffer */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 8, - &hdev->posbuffer); + /* TODO: check postion buffer update */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 8 * num_total, + &bus->posbuf); if (ret < 0) { dev_err(sdev->dev, "error: posbuffer dma alloc failed\n"); return -ENOMEM; } - if (sdev->hbus) { - /* mem alloc for the CORB/RIRB ringbuffers */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, - PAGE_SIZE, &sdev->hbus->core.rb); - if (ret < 0) { - dev_err(sdev->dev, "error: RB alloc failed\n"); - return -ENOMEM; - } + + /* mem alloc for the CORB/RIRB ringbuffers */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, + PAGE_SIZE, &bus->rb); + if (ret < 0) { + dev_err(sdev->dev, "error: RB alloc failed\n"); + return -ENOMEM; } /* create capture streams */ for (i = 0; i < num_capture; i++) { - stream = &hdev->cstream[i]; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; @@ -598,15 +619,21 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) HDA_DSP_BDL_SIZE, &stream->bdl); if (ret < 0) { dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + kfree(stream); return -ENOMEM; } - stream->posbuf = (__le32 *)(hdev->posbuffer.area + + stream->posbuf = (__le32 *)(bus->posbuf.area + (stream->index) * 8); + + list_add_tail(&stream->list, &bus->stream_list); } /* create playback streams */ for (i = num_capture; i < num_total; i++) { - stream = &hdev->pstream[i - num_capture]; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; /* we always have DSP support */ stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + @@ -646,11 +673,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) HDA_DSP_BDL_SIZE, &stream->bdl); if (ret < 0) { dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); + kfree(stream); return -ENOMEM; } - stream->posbuf = (__le32 *)(hdev->posbuffer.area + + stream->posbuf = (__le32 *)(bus->posbuf.area + (stream->index) * 8); + + list_add_tail(&stream->list, &bus->stream_list); } return 0; @@ -658,30 +688,22 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) void hda_dsp_stream_free(struct snd_sof_dev *sdev) { - struct sof_intel_hda_dev *hdev = sdev->hda; - struct sof_intel_hda_stream *stream; - int i; - - /* free position buffer */ - if (hdev->posbuffer.area) - snd_dma_free_pages(&hdev->posbuffer); + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *s, *_s; - /* free capture streams */ - for (i = 0; i < hdev->num_capture; i++) { - stream = &hdev->cstream[i]; - /* free bdl buffer */ - if (stream->bdl.area) - snd_dma_free_pages(&stream->bdl); - } + /* free position buffer */ + if (bus->posbuf.area) + snd_dma_free_pages(&bus->posbuf); - /* free playback streams */ - for (i = 0; i < hdev->num_playback; i++) { - stream = &hdev->pstream[i]; + list_for_each_entry_safe(s, _s, &bus->stream_list, list) { + /* TODO: decouple */ /* free bdl buffer */ - if (stream->bdl.area) - snd_dma_free_pages(&stream->bdl); + if (s->bdl.area) + snd_dma_free_pages(&s->bdl); + list_del(&s->list); + kfree(s); } } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index a65dca8c446326..1965967cc7f981 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -342,12 +342,8 @@ static int hda_init(struct snd_sof_dev *sdev) //int err; //unsigned short gcap; - hbus = devm_kzalloc(&pci->dev, sizeof(*hbus), GFP_KERNEL); - if (!hbus) - return -ENOMEM; - - sdev->hbus = hbus; - bus = &hbus->core; + hbus = sof_to_hbus(sdev); + bus = sof_to_bus(sdev); /* HDA bus init */ #if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDA) @@ -389,7 +385,7 @@ static int hda_init(struct snd_sof_dev *sdev) static int hda_init_caps(struct snd_sof_dev *sdev) { - struct hdac_bus *bus = &sdev->hbus->core; + struct hdac_bus *bus = sof_to_bus(sdev); struct pci_dev *pci = sdev->pci; struct hdac_ext_link *hlink = NULL; int ret = 0; @@ -499,9 +495,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = sdev->pci; struct sof_intel_hda_dev *hdev; + struct hdac_bus *bus; struct sof_intel_hda_stream *stream; const struct sof_intel_dsp_desc *chip; - int i; int ret = 0; /* set DSP arch ops */ @@ -582,20 +578,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) } /* clear stream status */ - for (i = 0 ; i < hdev->num_capture ; i++) { - stream = &hdev->cstream[i]; - if (stream) - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - stream->sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_CL_DMA_SD_INT_MASK); - } - - for (i = 0 ; i < hdev->num_playback ; i++) { - stream = &hdev->pstream[i]; - if (stream) - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + bus = sof_to_bus(sdev); + list_for_each_entry(stream, &bus->stream_list, list) { + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, stream->sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 6ff873f66be456..a9aa7f6cf98b3d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -358,6 +358,8 @@ struct sof_intel_hda_stream { /* PCM */ struct snd_pcm_substream *substream; + + struct list_head list; /* list of streams on the bus */ }; #define SOF_HDA_PLAYBACK_STREAMS 16 @@ -368,18 +370,11 @@ struct sof_intel_hda_stream { /* represents DSP HDA controller frontend - i.e. host facing control */ struct sof_intel_hda_dev { + struct hda_bus hbus; + /* hw config */ const struct sof_intel_dsp_desc *desc; - /* streams */ - struct sof_intel_hda_stream pstream[SOF_HDA_PLAYBACK_STREAMS]; - struct sof_intel_hda_stream cstream[SOF_HDA_CAPTURE_STREAMS]; - int num_capture; - int num_playback; - - /* position buffers */ - struct snd_dma_buffer posbuffer; - /*trace */ struct sof_intel_hda_stream *dtrace_stream; @@ -453,10 +448,14 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct sof_intel_hda_stream *stream, struct sof_intel_dsp_bdl *bdl, int size, struct snd_pcm_hw_params *params); + +struct sof_intel_hda_stream * + hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction); struct sof_intel_hda_stream * hda_dsp_stream_get_cstream(struct snd_sof_dev *sdev); struct sof_intel_hda_stream * hda_dsp_stream_get_pstream(struct snd_sof_dev *sdev); +int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag); int hda_dsp_stream_put_pstream(struct snd_sof_dev *sdev, int stream_tag); int hda_dsp_stream_put_cstream(struct snd_sof_dev *sdev, int stream_tag); int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e1c2d1deb5f667..faa56f2e29bcd7 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -298,7 +298,6 @@ struct snd_sof_dev { struct snd_sof_pdata *pdata; const struct snd_sof_dsp_ops *ops; struct sof_intel_hda_dev *hda; /* for HDA based DSP HW FIXME: delete this and use hbus instead */ - struct hda_bus *hbus; const struct sof_arch_ops *arch_ops; /* IPC */ @@ -355,6 +354,9 @@ struct snd_sof_dev { void *private; /* core does not touch this */ }; +#define sof_to_bus(s) (&(s)->hda->hbus.core) +#define sof_to_hbus(s) (&(s)->hda->hbus) + /* * SOF platform private struct used as drvdata of * platform dev (e.g. pci/acpi/spi...) drvdata.