cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cobalt-alsa-pcm.c (12767B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  ALSA PCM device for the
      4 *  ALSA interface to cobalt PCM capture streams
      5 *
      6 *  Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
      7 *  All rights reserved.
      8 */
      9
     10#include <linux/init.h>
     11#include <linux/kernel.h>
     12#include <linux/delay.h>
     13
     14#include <media/v4l2-device.h>
     15
     16#include <sound/core.h>
     17#include <sound/pcm.h>
     18
     19#include "cobalt-driver.h"
     20#include "cobalt-alsa.h"
     21#include "cobalt-alsa-pcm.h"
     22
     23static unsigned int pcm_debug;
     24module_param(pcm_debug, int, 0644);
     25MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
     26
     27#define dprintk(fmt, arg...) \
     28	do { \
     29		if (pcm_debug) \
     30			pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
     31	} while (0)
     32
     33static const struct snd_pcm_hardware snd_cobalt_hdmi_capture = {
     34	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
     35		SNDRV_PCM_INFO_MMAP           |
     36		SNDRV_PCM_INFO_INTERLEAVED    |
     37		SNDRV_PCM_INFO_MMAP_VALID,
     38
     39	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
     40
     41	.rates = SNDRV_PCM_RATE_48000,
     42
     43	.rate_min = 48000,
     44	.rate_max = 48000,
     45	.channels_min = 1,
     46	.channels_max = 8,
     47	.buffer_bytes_max = 4 * 240 * 8 * 4,	/* 5 ms of data */
     48	.period_bytes_min = 1920,		/* 1 sample = 8 * 4 bytes */
     49	.period_bytes_max = 240 * 8 * 4,	/* 5 ms of 8 channel data */
     50	.periods_min = 1,
     51	.periods_max = 4,
     52};
     53
     54static const struct snd_pcm_hardware snd_cobalt_playback = {
     55	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
     56		SNDRV_PCM_INFO_MMAP           |
     57		SNDRV_PCM_INFO_INTERLEAVED    |
     58		SNDRV_PCM_INFO_MMAP_VALID,
     59
     60	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
     61
     62	.rates = SNDRV_PCM_RATE_48000,
     63
     64	.rate_min = 48000,
     65	.rate_max = 48000,
     66	.channels_min = 1,
     67	.channels_max = 8,
     68	.buffer_bytes_max = 4 * 240 * 8 * 4,	/* 5 ms of data */
     69	.period_bytes_min = 1920,		/* 1 sample = 8 * 4 bytes */
     70	.period_bytes_max = 240 * 8 * 4,	/* 5 ms of 8 channel data */
     71	.periods_min = 1,
     72	.periods_max = 4,
     73};
     74
     75static void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
     76{
     77	static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
     78	unsigned idx = 0;
     79
     80	while (len >= (is_s32 ? 4 : 2)) {
     81		unsigned offset = map[idx] * 4;
     82		u32 val = src[offset + 1] + (src[offset + 2] << 8) +
     83			 (src[offset + 3] << 16);
     84
     85		if (is_s32) {
     86			*dst++ = 0;
     87			*dst++ = val & 0xff;
     88		}
     89		*dst++ = (val >> 8) & 0xff;
     90		*dst++ = (val >> 16) & 0xff;
     91		len -= is_s32 ? 4 : 2;
     92		idx++;
     93	}
     94}
     95
     96static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc,
     97					u8 *pcm_data,
     98					size_t skip,
     99					size_t samples)
    100{
    101	struct snd_pcm_substream *substream;
    102	struct snd_pcm_runtime *runtime;
    103	unsigned long flags;
    104	unsigned int oldptr;
    105	unsigned int stride;
    106	int length = samples;
    107	int period_elapsed = 0;
    108	bool is_s32;
    109
    110	dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc,
    111		pcm_data, samples);
    112
    113	substream = cobsc->capture_pcm_substream;
    114	if (substream == NULL) {
    115		dprintk("substream was NULL\n");
    116		return;
    117	}
    118
    119	runtime = substream->runtime;
    120	if (runtime == NULL) {
    121		dprintk("runtime was NULL\n");
    122		return;
    123	}
    124	is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
    125
    126	stride = runtime->frame_bits >> 3;
    127	if (stride == 0) {
    128		dprintk("stride is zero\n");
    129		return;
    130	}
    131
    132	if (length == 0) {
    133		dprintk("%s: length was zero\n", __func__);
    134		return;
    135	}
    136
    137	if (runtime->dma_area == NULL) {
    138		dprintk("dma area was NULL - ignoring\n");
    139		return;
    140	}
    141
    142	oldptr = cobsc->hwptr_done_capture;
    143	if (oldptr + length >= runtime->buffer_size) {
    144		unsigned int cnt = runtime->buffer_size - oldptr;
    145		unsigned i;
    146
    147		for (i = 0; i < cnt; i++)
    148			sample_cpy(runtime->dma_area + (oldptr + i) * stride,
    149					pcm_data + i * skip,
    150					stride, is_s32);
    151		for (i = cnt; i < length; i++)
    152			sample_cpy(runtime->dma_area + (i - cnt) * stride,
    153					pcm_data + i * skip, stride, is_s32);
    154	} else {
    155		unsigned i;
    156
    157		for (i = 0; i < length; i++)
    158			sample_cpy(runtime->dma_area + (oldptr + i) * stride,
    159					pcm_data + i * skip,
    160					stride, is_s32);
    161	}
    162	snd_pcm_stream_lock_irqsave(substream, flags);
    163
    164	cobsc->hwptr_done_capture += length;
    165	if (cobsc->hwptr_done_capture >=
    166	    runtime->buffer_size)
    167		cobsc->hwptr_done_capture -=
    168			runtime->buffer_size;
    169
    170	cobsc->capture_transfer_done += length;
    171	if (cobsc->capture_transfer_done >=
    172	    runtime->period_size) {
    173		cobsc->capture_transfer_done -=
    174			runtime->period_size;
    175		period_elapsed = 1;
    176	}
    177
    178	snd_pcm_stream_unlock_irqrestore(substream, flags);
    179
    180	if (period_elapsed)
    181		snd_pcm_period_elapsed(substream);
    182}
    183
    184static int alsa_fnc(struct vb2_buffer *vb, void *priv)
    185{
    186	struct cobalt_stream *s = priv;
    187	unsigned char *p = vb2_plane_vaddr(vb, 0);
    188	int i;
    189
    190	if (pcm_debug) {
    191		pr_info("alsa: ");
    192		for (i = 0; i < 8 * 4; i++) {
    193			if (!(i & 3))
    194				pr_cont(" ");
    195			pr_cont("%02x", p[i]);
    196		}
    197		pr_cont("\n");
    198	}
    199	cobalt_alsa_announce_pcm_data(s->alsa,
    200			vb2_plane_vaddr(vb, 0),
    201			8 * 4,
    202			vb2_get_plane_payload(vb, 0) / (8 * 4));
    203	return 0;
    204}
    205
    206static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream)
    207{
    208	struct snd_pcm_runtime *runtime = substream->runtime;
    209	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
    210	struct cobalt_stream *s = cobsc->s;
    211
    212	runtime->hw = snd_cobalt_hdmi_capture;
    213	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
    214	cobsc->capture_pcm_substream = substream;
    215	runtime->private_data = s;
    216	cobsc->alsa_record_cnt++;
    217	if (cobsc->alsa_record_cnt == 1) {
    218		int rc;
    219
    220		rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name);
    221		if (rc) {
    222			cobsc->alsa_record_cnt--;
    223			return rc;
    224		}
    225	}
    226	return 0;
    227}
    228
    229static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream)
    230{
    231	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
    232	struct cobalt_stream *s = cobsc->s;
    233
    234	cobsc->alsa_record_cnt--;
    235	if (cobsc->alsa_record_cnt == 0)
    236		vb2_thread_stop(&s->q);
    237	return 0;
    238}
    239
    240static int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream)
    241{
    242	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
    243
    244	cobsc->hwptr_done_capture = 0;
    245	cobsc->capture_transfer_done = 0;
    246
    247	return 0;
    248}
    249
    250static int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
    251{
    252	switch (cmd) {
    253	case SNDRV_PCM_TRIGGER_START:
    254	case SNDRV_PCM_TRIGGER_STOP:
    255		return 0;
    256	default:
    257		return -EINVAL;
    258	}
    259	return 0;
    260}
    261
    262static
    263snd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream)
    264{
    265	snd_pcm_uframes_t hwptr_done;
    266	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
    267
    268	hwptr_done = cobsc->hwptr_done_capture;
    269
    270	return hwptr_done;
    271}
    272
    273static void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
    274{
    275	static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
    276	unsigned idx = 0;
    277
    278	while (len >= (is_s32 ? 4 : 2)) {
    279		unsigned offset = map[idx] * 4;
    280		u8 *out = dst + offset;
    281
    282		*out++ = 0;
    283		if (is_s32) {
    284			src++;
    285			*out++ = *src++;
    286		} else {
    287			*out++ = 0;
    288		}
    289		*out++ = *src++;
    290		*out = *src++;
    291		len -= is_s32 ? 4 : 2;
    292		idx++;
    293	}
    294}
    295
    296static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc,
    297					u8 *pcm_data,
    298					size_t skip,
    299					size_t samples)
    300{
    301	struct snd_pcm_substream *substream;
    302	struct snd_pcm_runtime *runtime;
    303	unsigned long flags;
    304	unsigned int pos;
    305	unsigned int stride;
    306	bool is_s32;
    307	unsigned i;
    308
    309	dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc,
    310		pcm_data, samples);
    311
    312	substream = cobsc->playback_pcm_substream;
    313	if (substream == NULL) {
    314		dprintk("substream was NULL\n");
    315		return;
    316	}
    317
    318	runtime = substream->runtime;
    319	if (runtime == NULL) {
    320		dprintk("runtime was NULL\n");
    321		return;
    322	}
    323
    324	is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
    325	stride = runtime->frame_bits >> 3;
    326	if (stride == 0) {
    327		dprintk("stride is zero\n");
    328		return;
    329	}
    330
    331	if (samples == 0) {
    332		dprintk("%s: samples was zero\n", __func__);
    333		return;
    334	}
    335
    336	if (runtime->dma_area == NULL) {
    337		dprintk("dma area was NULL - ignoring\n");
    338		return;
    339	}
    340
    341	pos = cobsc->pb_pos % cobsc->pb_size;
    342	for (i = 0; i < cobsc->pb_count / (8 * 4); i++)
    343		pb_sample_cpy(pcm_data + i * skip,
    344				runtime->dma_area + pos + i * stride,
    345				stride, is_s32);
    346	snd_pcm_stream_lock_irqsave(substream, flags);
    347
    348	cobsc->pb_pos += i * stride;
    349
    350	snd_pcm_stream_unlock_irqrestore(substream, flags);
    351	if (cobsc->pb_pos % cobsc->pb_count == 0)
    352		snd_pcm_period_elapsed(substream);
    353}
    354
    355static int alsa_pb_fnc(struct vb2_buffer *vb, void *priv)
    356{
    357	struct cobalt_stream *s = priv;
    358
    359	if (s->alsa->alsa_pb_channel)
    360		cobalt_alsa_pb_pcm_data(s->alsa,
    361				vb2_plane_vaddr(vb, 0),
    362				8 * 4,
    363				vb2_get_plane_payload(vb, 0) / (8 * 4));
    364	return 0;
    365}
    366
    367static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream)
    368{
    369	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
    370	struct snd_pcm_runtime *runtime = substream->runtime;
    371	struct cobalt_stream *s = cobsc->s;
    372
    373	runtime->hw = snd_cobalt_playback;
    374	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
    375	cobsc->playback_pcm_substream = substream;
    376	runtime->private_data = s;
    377	cobsc->alsa_playback_cnt++;
    378	if (cobsc->alsa_playback_cnt == 1) {
    379		int rc;
    380
    381		rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name);
    382		if (rc) {
    383			cobsc->alsa_playback_cnt--;
    384			return rc;
    385		}
    386	}
    387
    388	return 0;
    389}
    390
    391static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream)
    392{
    393	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
    394	struct cobalt_stream *s = cobsc->s;
    395
    396	cobsc->alsa_playback_cnt--;
    397	if (cobsc->alsa_playback_cnt == 0)
    398		vb2_thread_stop(&s->q);
    399	return 0;
    400}
    401
    402static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream)
    403{
    404	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
    405
    406	cobsc->pb_size = snd_pcm_lib_buffer_bytes(substream);
    407	cobsc->pb_count = snd_pcm_lib_period_bytes(substream);
    408	cobsc->pb_pos = 0;
    409
    410	return 0;
    411}
    412
    413static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream,
    414				     int cmd)
    415{
    416	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
    417
    418	switch (cmd) {
    419	case SNDRV_PCM_TRIGGER_START:
    420		if (cobsc->alsa_pb_channel)
    421			return -EBUSY;
    422		cobsc->alsa_pb_channel = true;
    423		return 0;
    424	case SNDRV_PCM_TRIGGER_STOP:
    425		cobsc->alsa_pb_channel = false;
    426		return 0;
    427	default:
    428		return -EINVAL;
    429	}
    430}
    431
    432static
    433snd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream)
    434{
    435	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
    436	size_t ptr;
    437
    438	ptr = cobsc->pb_pos;
    439
    440	return bytes_to_frames(substream->runtime, ptr) %
    441	       substream->runtime->buffer_size;
    442}
    443
    444static const struct snd_pcm_ops snd_cobalt_pcm_capture_ops = {
    445	.open		= snd_cobalt_pcm_capture_open,
    446	.close		= snd_cobalt_pcm_capture_close,
    447	.prepare	= snd_cobalt_pcm_prepare,
    448	.trigger	= snd_cobalt_pcm_trigger,
    449	.pointer	= snd_cobalt_pcm_pointer,
    450};
    451
    452static const struct snd_pcm_ops snd_cobalt_pcm_playback_ops = {
    453	.open		= snd_cobalt_pcm_playback_open,
    454	.close		= snd_cobalt_pcm_playback_close,
    455	.prepare	= snd_cobalt_pcm_pb_prepare,
    456	.trigger	= snd_cobalt_pcm_pb_trigger,
    457	.pointer	= snd_cobalt_pcm_pb_pointer,
    458};
    459
    460int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc)
    461{
    462	struct snd_pcm *sp;
    463	struct snd_card *sc = cobsc->sc;
    464	struct cobalt_stream *s = cobsc->s;
    465	struct cobalt *cobalt = s->cobalt;
    466	int ret;
    467
    468	s->q.gfp_flags |= __GFP_ZERO;
    469
    470	if (!s->is_output) {
    471		cobalt_s_bit_sysctrl(cobalt,
    472			COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
    473			0);
    474		mdelay(2);
    475		cobalt_s_bit_sysctrl(cobalt,
    476			COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
    477			1);
    478		mdelay(1);
    479
    480		ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI",
    481			0, /* PCM device 0, the only one for this card */
    482			0, /* 0 playback substreams */
    483			1, /* 1 capture substream */
    484			&sp);
    485		if (ret) {
    486			cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n",
    487				   ret);
    488			goto err_exit;
    489		}
    490
    491		snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
    492				&snd_cobalt_pcm_capture_ops);
    493		snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC,
    494					       NULL, 0, 0);
    495		sp->info_flags = 0;
    496		sp->private_data = cobsc;
    497		strscpy(sp->name, "cobalt", sizeof(sp->name));
    498	} else {
    499		cobalt_s_bit_sysctrl(cobalt,
    500			COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0);
    501		mdelay(2);
    502		cobalt_s_bit_sysctrl(cobalt,
    503			COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 1);
    504		mdelay(1);
    505
    506		ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI",
    507			0, /* PCM device 0, the only one for this card */
    508			1, /* 0 playback substreams */
    509			0, /* 1 capture substream */
    510			&sp);
    511		if (ret) {
    512			cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n",
    513				   ret);
    514			goto err_exit;
    515		}
    516
    517		snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK,
    518				&snd_cobalt_pcm_playback_ops);
    519		snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC,
    520					       NULL, 0, 0);
    521		sp->info_flags = 0;
    522		sp->private_data = cobsc;
    523		strscpy(sp->name, "cobalt", sizeof(sp->name));
    524	}
    525
    526	return 0;
    527
    528err_exit:
    529	return ret;
    530}