On 2020-09-21 4:08 PM, Andy Shevchenko wrote:
On Mon, Sep 21, 2020 at 01:54:16PM +0200, Cezary Rojewski wrote:
DSP designed for Lynxpoint and Wildcat Point offers no dynamic topology i.e. all pipelines are already defined within firmware and host is relegated to allocing stream for predefined pins. This is represented by 'catpt_topology' member.
...
+static void catpt_arrange_page_table(struct snd_pcm_substream *substream,
struct snd_dma_buffer *pgtbl)
+{
- struct snd_pcm_runtime *rtm = substream->runtime;
- struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream);
- int i, pages;
- pages = snd_sgbuf_aligned_pages(rtm->dma_bytes);
- for (i = 0; i < pages; i++) {
u32 pfn, offset;
u32 *page_table;
pfn = snd_sgbuf_get_addr(databuf, i * PAGE_SIZE) >> PAGE_SHIFT;
PFN_DOWN()
Ack. Updating in both cases where explicit right shift by PAGE_SHIFT is used.
/* incrementing by 2 on even and 3 on odd */
offset = ((i << 2) + i) >> 1;
page_table = (u32 *)(pgtbl->area + offset);
if (i & 1)
*page_table |= (pfn << 4);
else
*page_table |= pfn;
- }
+}
+static u32 catpt_get_channel_map(enum catpt_channel_config config) +{
- switch (config) {
- case CATPT_CHANNEL_CONFIG_MONO:
return (0xFFFFFFF0 | CATPT_CHANNEL_CENTER);
- case CATPT_CHANNEL_CONFIG_STEREO:
return (0xFFFFFF00 | CATPT_CHANNEL_LEFT
| (CATPT_CHANNEL_RIGHT << 4));
- case CATPT_CHANNEL_CONFIG_2_POINT_1:
return (0xFFFFF000 | CATPT_CHANNEL_LEFT
| (CATPT_CHANNEL_RIGHT << 4)
| (CATPT_CHANNEL_LFE << 8));
- case CATPT_CHANNEL_CONFIG_3_POINT_0:
return (0xFFFFF000 | CATPT_CHANNEL_LEFT
| (CATPT_CHANNEL_CENTER << 4)
| (CATPT_CHANNEL_RIGHT << 8));
- case CATPT_CHANNEL_CONFIG_3_POINT_1:
return (0xFFFF0000 | CATPT_CHANNEL_LEFT
| (CATPT_CHANNEL_CENTER << 4)
| (CATPT_CHANNEL_RIGHT << 8)
| (CATPT_CHANNEL_LFE << 12));
- case CATPT_CHANNEL_CONFIG_QUATRO:
return (0xFFFF0000 | CATPT_CHANNEL_LEFT
| (CATPT_CHANNEL_RIGHT << 4)
| (CATPT_CHANNEL_LEFT_SURROUND << 8)
| (CATPT_CHANNEL_RIGHT_SURROUND << 12));
- case CATPT_CHANNEL_CONFIG_4_POINT_0:
return (0xFFFF0000 | CATPT_CHANNEL_LEFT
| (CATPT_CHANNEL_CENTER << 4)
| (CATPT_CHANNEL_RIGHT << 8)
| (CATPT_CHANNEL_CENTER_SURROUND << 12));
- case CATPT_CHANNEL_CONFIG_5_POINT_0:
return (0xFFF00000 | CATPT_CHANNEL_LEFT
| (CATPT_CHANNEL_CENTER << 4)
| (CATPT_CHANNEL_RIGHT << 8)
| (CATPT_CHANNEL_LEFT_SURROUND << 12)
| (CATPT_CHANNEL_RIGHT_SURROUND << 16));
- case CATPT_CHANNEL_CONFIG_5_POINT_1:
return (0xFF000000 | CATPT_CHANNEL_CENTER
| (CATPT_CHANNEL_LEFT << 4)
| (CATPT_CHANNEL_RIGHT << 8)
| (CATPT_CHANNEL_LEFT_SURROUND << 12)
| (CATPT_CHANNEL_RIGHT_SURROUND << 16)
| (CATPT_CHANNEL_LFE << 20));
- case CATPT_CHANNEL_CONFIG_DUAL_MONO:
return (0xFFFFFF00 | CATPT_CHANNEL_LEFT
| (CATPT_CHANNEL_LEFT << 4));
- default:
return 0xFFFFFFFF;
- }
GENMASK() for all above?
Ack. GENMASK + U32_MAX for the 'default' label.
+}
+static enum catpt_channel_config catpt_get_channel_config(u32 num_channels) +{
- switch (num_channels) {
- case 6:
return CATPT_CHANNEL_CONFIG_5_POINT_1;
- case 5:
return CATPT_CHANNEL_CONFIG_5_POINT_0;
- case 4:
return CATPT_CHANNEL_CONFIG_QUATRO;
- case 3:
return CATPT_CHANNEL_CONFIG_2_POINT_1;
- case 1:
return CATPT_CHANNEL_CONFIG_MONO;
- case 2:
- default:
return CATPT_CHANNEL_CONFIG_STEREO;
- }
+}
+static int catpt_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
+{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
- struct catpt_stream_template *template;
- struct catpt_stream_runtime *stream;
- struct resource *res;
- int ret;
- template = catpt_get_stream_template(substream);
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
return -ENOMEM;
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, cdev->dev, PAGE_SIZE,
&stream->pgtbl);
- if (ret)
goto err_pgtbl;
- res = catpt_request_region(&cdev->dram, template->persistent_size);
- if (!res) {
ret = -EBUSY;
goto err_request;
- }
- catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
- stream->template = template;
- stream->persistent = res;
- stream->substream = substream;
- INIT_LIST_HEAD(&stream->node);
- snd_soc_dai_set_dma_data(dai, substream, stream);
- spin_lock(&cdev->list_lock);
- list_add_tail(&stream->node, &cdev->stream_list);
- spin_unlock(&cdev->list_lock);
- return 0;
+err_request:
- snd_dma_free_pages(&stream->pgtbl);
+err_pgtbl:
- kfree(stream);
- return ret;
+}
+static void catpt_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
+{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
- struct catpt_stream_runtime *stream;
- stream = snd_soc_dai_get_dma_data(dai, substream);
- spin_lock(&cdev->list_lock);
- list_del(&stream->node);
- spin_unlock(&cdev->list_lock);
- release_resource(stream->persistent);
- kfree(stream->persistent);
- catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
- snd_dma_free_pages(&stream->pgtbl);
- kfree(stream);
- snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
+{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
- struct catpt_stream_runtime *stream;
- struct catpt_audio_format afmt;
- struct catpt_ring_info rinfo;
- struct snd_pcm_runtime *rtm = substream->runtime;
- struct snd_dma_buffer *dmab;
- int ret;
- stream = snd_soc_dai_get_dma_data(dai, substream);
- if (stream->allocated)
return 0;
- memset(&afmt, 0, sizeof(afmt));
- afmt.sample_rate = params_rate(params);
- afmt.bit_depth = params_physical_width(params);
- afmt.valid_bit_depth = params_width(params);
- afmt.num_channels = params_channels(params);
- afmt.channel_config = catpt_get_channel_config(afmt.num_channels);
- afmt.channel_map = catpt_get_channel_map(afmt.channel_config);
- afmt.interleaving = CATPT_INTERLEAVING_PER_CHANNEL;
- dmab = snd_pcm_get_dma_buf(substream);
- catpt_arrange_page_table(substream, &stream->pgtbl);
- memset(&rinfo, 0, sizeof(rinfo));
- rinfo.page_table_addr = stream->pgtbl.addr;
- rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE);
- rinfo.size = rtm->dma_bytes;
- rinfo.offset = 0;
- rinfo.ring_first_page_pfn = snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT;
- ret = catpt_ipc_alloc_stream(cdev, stream->template->path_id,
stream->template->type,
&afmt, &rinfo,
stream->template->num_entries,
stream->template->entries,
stream->persistent,
cdev->scratch,
&stream->info);
- if (ret)
return CATPT_IPC_ERROR(ret);
- stream->allocated = true;
- return 0;
+}
+static int catpt_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
+{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
- struct catpt_stream_runtime *stream;
- stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream->allocated)
return 0;
- catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
- catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);
- stream->allocated = false;
- return 0;
+}
+static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol);
+static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,
struct catpt_stream_runtime *stream)
+{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
- struct snd_soc_component *component = dai->component;
- struct snd_kcontrol *pos, *kctl = NULL;
- const char *name;
- int ret;
- u32 id = stream->info.stream_hw_id;
- /* only selected streams have individual controls */
- switch (id) {
- case CATPT_PIN_ID_OFFLOAD1:
name = "Media0 Playback Volume";
break;
- case CATPT_PIN_ID_OFFLOAD2:
name = "Media1 Playback Volume";
break;
- case CATPT_PIN_ID_CAPTURE1:
name = "Mic Capture Volume";
break;
- case CATPT_PIN_ID_REFERENCE:
name = "Loopback Mute";
break;
- default:
return 0;
- };
- list_for_each_entry(pos, &component->card->snd_card->controls, list) {
if (pos->private_data == component &&
!strncmp(name, pos->id.name, sizeof(pos->id.name))) {
kctl = pos;
break;
}
- }
- if (!kctl)
return -ENOENT;
- if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK)
return catpt_set_dspvol(cdev, id, (long *)kctl->private_value);
- ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)kctl->private_value);
- if (ret)
return CATPT_IPC_ERROR(ret);
- return 0;
+}
+static int catpt_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
+{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
- struct catpt_stream_runtime *stream;
- int ret;
- stream = snd_soc_dai_get_dma_data(dai, substream);
- if (stream->prepared)
return 0;
- ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
- if (ret)
return CATPT_IPC_ERROR(ret);
- ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
- if (ret)
return CATPT_IPC_ERROR(ret);
- ret = catpt_dsp_update_lpclock(cdev);
- if (ret)
return ret;
- ret = catpt_dai_apply_usettings(dai, stream);
- if (ret)
return ret;
- stream->prepared = true;
- return 0;
+}
+static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
+{
- struct catpt_dev *cdev = dev_get_drvdata(dai->dev);
- struct catpt_stream_runtime *stream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t pos;
- int ret;
- stream = snd_soc_dai_get_dma_data(dai, substream);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
/* only offload is set_write_pos driven */
if (stream->template->type != CATPT_STRM_TYPE_RENDER)
goto resume_stream;
pos = frames_to_bytes(runtime, runtime->start_threshold);
/*
* Dsp operates on buffer halves, thus max 2x set_write_pos
* (entire buffer filled) prior to stream start.
*/
ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
pos, false, false);
if (ret)
return CATPT_IPC_ERROR(ret);
fallthrough;
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- resume_stream:
ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id);
if (ret)
return CATPT_IPC_ERROR(ret);
break;
- case SNDRV_PCM_TRIGGER_STOP:
stream->prepared = false;
catpt_dsp_update_lpclock(cdev);
fallthrough;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
if (ret)
return CATPT_IPC_ERROR(ret);
break;
- default:
break;
- }
- return 0;
+}
+void catpt_stream_update_position(struct catpt_dev *cdev,
struct catpt_stream_runtime *stream,
struct catpt_notify_position *pos)
+{
- struct snd_pcm_substream *substream = stream->substream;
- struct snd_pcm_runtime *r = substream->runtime;
- snd_pcm_uframes_t dsppos, newpos;
- int ret;
- dsppos = bytes_to_frames(r, pos->stream_position);
- /* only offload is set_write_pos driven */
- if (stream->template->type != CATPT_STRM_TYPE_RENDER)
goto exit;
- if (dsppos >= r->buffer_size / 2)
newpos = r->buffer_size / 2;
- else
newpos = 0;
- /*
* Dsp operates on buffer halves, thus on every notify position
* (buffer half consumed) update wp to allow stream progression.
*/
- ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id,
frames_to_bytes(r, newpos),
false, false);
- if (ret) {
dev_err(cdev->dev, "update position for stream %d failed: %d\n",
stream->info.stream_hw_id, ret);
return;
- }
+exit:
- snd_pcm_period_elapsed(substream);
+}
+#define CATPT_BUFFER_MAX_SIZE 76800 /* 200ms native format */
I don't understand 'native format'. Please, give clearer comment, like
/* 200ms for 8 8-bit channels at 48kHz (native format) */
Sure, will expand the description. Just so you know, it's 2/24(32)/48kHz for LPT/WPT solution.
+#define CATPT_PCM_PERIODS_MAX 4 +#define CATPT_PCM_PERIODS_MIN 2
...
+#define DSP_VOLUME_MAX 0x7FFFFFFF /* 0db */
S32_MAX ?
Ack.
+static const u32 volume_map[] = {
- DSP_VOLUME_MAX >> 30,
- DSP_VOLUME_MAX >> 29,
- DSP_VOLUME_MAX >> 28,
- DSP_VOLUME_MAX >> 27,
- DSP_VOLUME_MAX >> 26,
- DSP_VOLUME_MAX >> 25,
- DSP_VOLUME_MAX >> 24,
- DSP_VOLUME_MAX >> 23,
- DSP_VOLUME_MAX >> 22,
- DSP_VOLUME_MAX >> 21,
- DSP_VOLUME_MAX >> 20,
- DSP_VOLUME_MAX >> 19,
- DSP_VOLUME_MAX >> 18,
- DSP_VOLUME_MAX >> 17,
- DSP_VOLUME_MAX >> 16,
- DSP_VOLUME_MAX >> 15,
- DSP_VOLUME_MAX >> 14,
- DSP_VOLUME_MAX >> 13,
- DSP_VOLUME_MAX >> 12,
- DSP_VOLUME_MAX >> 11,
- DSP_VOLUME_MAX >> 10,
- DSP_VOLUME_MAX >> 9,
- DSP_VOLUME_MAX >> 8,
- DSP_VOLUME_MAX >> 7,
- DSP_VOLUME_MAX >> 6,
- DSP_VOLUME_MAX >> 5,
- DSP_VOLUME_MAX >> 4,
- DSP_VOLUME_MAX >> 3,
- DSP_VOLUME_MAX >> 2,
- DSP_VOLUME_MAX >> 1,
- DSP_VOLUME_MAX >> 0,
+};
Why do you have table? Does it have any gaps? Why can't be formula used?
No gaps, just a lookup table. As catpt aims to keep 100% backward compatibility with what sound/soc/intel/haswell solution exposes, it is also useful for filling kcontrol info: catpt_volume_info() function.
+static u32 ctlvol_to_dspvol(u32 value) +{
- if (value >= ARRAY_SIZE(volume_map))
value = 0;
- return volume_map[value];
+}
+static u32 dspvol_to_ctlvol(u32 volume) +{
- int i;
- for (i = 0; i < ARRAY_SIZE(volume_map); i++)
if (volume_map[i] >= volume)
return i;
- return i - 1;
It seems okay in this case, but in general the code looks dangerous (for example, if ARRAY_SIZE is 0).
Agreed, although volume_map will never change so I've decided to drop additional safety.
...
+}
+static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol) +{
- bool all_equal = true;
- u32 dspvol;
- int ret, i;
- for (i = 1; all_equal && i < CATPT_CHANNELS_MAX; i++)
all_equal = (ctlvol[i] == ctlvol[0]);
for (i = 1; i < CATPT_CHANNELS_MAX; i++) if (ctlvol[i] != ctlvol[0]) break;
- if (all_equal) {
if (i == _MAX)
and one variable less.
Keen eye, thanks for this tip! Ack.
...
dspvol = ctlvol_to_dspvol(ctlvol[0]);
ret = catpt_ipc_set_volume(cdev, stream_id,
CATPT_ALL_CHANNELS_MASK, dspvol,
0, CATPT_AUDIO_CURVE_NONE);
- } else {
for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
dspvol = ctlvol_to_dspvol(ctlvol[i]);
ret = catpt_ipc_set_volume(cdev, stream_id,
i, dspvol,
0, CATPT_AUDIO_CURVE_NONE);
if (ret)
goto exit;
Don't see necessity of this in this patch...
}
- }
+exit:
...neither this.
Believe simple 'break' suffices. 'exit' label becomes redundant too.
Thanks, Czarek