0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/init.h>
0011 #include <linux/kernel.h>
0012 #include <linux/delay.h>
0013
0014 #include <media/v4l2-device.h>
0015
0016 #include <sound/core.h>
0017 #include <sound/pcm.h>
0018
0019 #include "cobalt-driver.h"
0020 #include "cobalt-alsa.h"
0021 #include "cobalt-alsa-pcm.h"
0022
0023 static unsigned int pcm_debug;
0024 module_param(pcm_debug, int, 0644);
0025 MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
0026
0027 #define dprintk(fmt, arg...) \
0028 do { \
0029 if (pcm_debug) \
0030 pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
0031 } while (0)
0032
0033 static const struct snd_pcm_hardware snd_cobalt_hdmi_capture = {
0034 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
0035 SNDRV_PCM_INFO_MMAP |
0036 SNDRV_PCM_INFO_INTERLEAVED |
0037 SNDRV_PCM_INFO_MMAP_VALID,
0038
0039 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
0040
0041 .rates = SNDRV_PCM_RATE_48000,
0042
0043 .rate_min = 48000,
0044 .rate_max = 48000,
0045 .channels_min = 1,
0046 .channels_max = 8,
0047 .buffer_bytes_max = 4 * 240 * 8 * 4,
0048 .period_bytes_min = 1920,
0049 .period_bytes_max = 240 * 8 * 4,
0050 .periods_min = 1,
0051 .periods_max = 4,
0052 };
0053
0054 static const struct snd_pcm_hardware snd_cobalt_playback = {
0055 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
0056 SNDRV_PCM_INFO_MMAP |
0057 SNDRV_PCM_INFO_INTERLEAVED |
0058 SNDRV_PCM_INFO_MMAP_VALID,
0059
0060 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
0061
0062 .rates = SNDRV_PCM_RATE_48000,
0063
0064 .rate_min = 48000,
0065 .rate_max = 48000,
0066 .channels_min = 1,
0067 .channels_max = 8,
0068 .buffer_bytes_max = 4 * 240 * 8 * 4,
0069 .period_bytes_min = 1920,
0070 .period_bytes_max = 240 * 8 * 4,
0071 .periods_min = 1,
0072 .periods_max = 4,
0073 };
0074
0075 static void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
0076 {
0077 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
0078 unsigned idx = 0;
0079
0080 while (len >= (is_s32 ? 4 : 2)) {
0081 unsigned offset = map[idx] * 4;
0082 u32 val = src[offset + 1] + (src[offset + 2] << 8) +
0083 (src[offset + 3] << 16);
0084
0085 if (is_s32) {
0086 *dst++ = 0;
0087 *dst++ = val & 0xff;
0088 }
0089 *dst++ = (val >> 8) & 0xff;
0090 *dst++ = (val >> 16) & 0xff;
0091 len -= is_s32 ? 4 : 2;
0092 idx++;
0093 }
0094 }
0095
0096 static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc,
0097 u8 *pcm_data,
0098 size_t skip,
0099 size_t samples)
0100 {
0101 struct snd_pcm_substream *substream;
0102 struct snd_pcm_runtime *runtime;
0103 unsigned long flags;
0104 unsigned int oldptr;
0105 unsigned int stride;
0106 int length = samples;
0107 int period_elapsed = 0;
0108 bool is_s32;
0109
0110 dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc,
0111 pcm_data, samples);
0112
0113 substream = cobsc->capture_pcm_substream;
0114 if (substream == NULL) {
0115 dprintk("substream was NULL\n");
0116 return;
0117 }
0118
0119 runtime = substream->runtime;
0120 if (runtime == NULL) {
0121 dprintk("runtime was NULL\n");
0122 return;
0123 }
0124 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
0125
0126 stride = runtime->frame_bits >> 3;
0127 if (stride == 0) {
0128 dprintk("stride is zero\n");
0129 return;
0130 }
0131
0132 if (length == 0) {
0133 dprintk("%s: length was zero\n", __func__);
0134 return;
0135 }
0136
0137 if (runtime->dma_area == NULL) {
0138 dprintk("dma area was NULL - ignoring\n");
0139 return;
0140 }
0141
0142 oldptr = cobsc->hwptr_done_capture;
0143 if (oldptr + length >= runtime->buffer_size) {
0144 unsigned int cnt = runtime->buffer_size - oldptr;
0145 unsigned i;
0146
0147 for (i = 0; i < cnt; i++)
0148 sample_cpy(runtime->dma_area + (oldptr + i) * stride,
0149 pcm_data + i * skip,
0150 stride, is_s32);
0151 for (i = cnt; i < length; i++)
0152 sample_cpy(runtime->dma_area + (i - cnt) * stride,
0153 pcm_data + i * skip, stride, is_s32);
0154 } else {
0155 unsigned i;
0156
0157 for (i = 0; i < length; i++)
0158 sample_cpy(runtime->dma_area + (oldptr + i) * stride,
0159 pcm_data + i * skip,
0160 stride, is_s32);
0161 }
0162 snd_pcm_stream_lock_irqsave(substream, flags);
0163
0164 cobsc->hwptr_done_capture += length;
0165 if (cobsc->hwptr_done_capture >=
0166 runtime->buffer_size)
0167 cobsc->hwptr_done_capture -=
0168 runtime->buffer_size;
0169
0170 cobsc->capture_transfer_done += length;
0171 if (cobsc->capture_transfer_done >=
0172 runtime->period_size) {
0173 cobsc->capture_transfer_done -=
0174 runtime->period_size;
0175 period_elapsed = 1;
0176 }
0177
0178 snd_pcm_stream_unlock_irqrestore(substream, flags);
0179
0180 if (period_elapsed)
0181 snd_pcm_period_elapsed(substream);
0182 }
0183
0184 static int alsa_fnc(struct vb2_buffer *vb, void *priv)
0185 {
0186 struct cobalt_stream *s = priv;
0187 unsigned char *p = vb2_plane_vaddr(vb, 0);
0188 int i;
0189
0190 if (pcm_debug) {
0191 pr_info("alsa: ");
0192 for (i = 0; i < 8 * 4; i++) {
0193 if (!(i & 3))
0194 pr_cont(" ");
0195 pr_cont("%02x", p[i]);
0196 }
0197 pr_cont("\n");
0198 }
0199 cobalt_alsa_announce_pcm_data(s->alsa,
0200 vb2_plane_vaddr(vb, 0),
0201 8 * 4,
0202 vb2_get_plane_payload(vb, 0) / (8 * 4));
0203 return 0;
0204 }
0205
0206 static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream)
0207 {
0208 struct snd_pcm_runtime *runtime = substream->runtime;
0209 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
0210 struct cobalt_stream *s = cobsc->s;
0211
0212 runtime->hw = snd_cobalt_hdmi_capture;
0213 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
0214 cobsc->capture_pcm_substream = substream;
0215 runtime->private_data = s;
0216 cobsc->alsa_record_cnt++;
0217 if (cobsc->alsa_record_cnt == 1) {
0218 int rc;
0219
0220 rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name);
0221 if (rc) {
0222 cobsc->alsa_record_cnt--;
0223 return rc;
0224 }
0225 }
0226 return 0;
0227 }
0228
0229 static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream)
0230 {
0231 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
0232 struct cobalt_stream *s = cobsc->s;
0233
0234 cobsc->alsa_record_cnt--;
0235 if (cobsc->alsa_record_cnt == 0)
0236 vb2_thread_stop(&s->q);
0237 return 0;
0238 }
0239
0240 static int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream)
0241 {
0242 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
0243
0244 cobsc->hwptr_done_capture = 0;
0245 cobsc->capture_transfer_done = 0;
0246
0247 return 0;
0248 }
0249
0250 static int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
0251 {
0252 switch (cmd) {
0253 case SNDRV_PCM_TRIGGER_START:
0254 case SNDRV_PCM_TRIGGER_STOP:
0255 return 0;
0256 default:
0257 return -EINVAL;
0258 }
0259 return 0;
0260 }
0261
0262 static
0263 snd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream)
0264 {
0265 snd_pcm_uframes_t hwptr_done;
0266 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
0267
0268 hwptr_done = cobsc->hwptr_done_capture;
0269
0270 return hwptr_done;
0271 }
0272
0273 static void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
0274 {
0275 static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
0276 unsigned idx = 0;
0277
0278 while (len >= (is_s32 ? 4 : 2)) {
0279 unsigned offset = map[idx] * 4;
0280 u8 *out = dst + offset;
0281
0282 *out++ = 0;
0283 if (is_s32) {
0284 src++;
0285 *out++ = *src++;
0286 } else {
0287 *out++ = 0;
0288 }
0289 *out++ = *src++;
0290 *out = *src++;
0291 len -= is_s32 ? 4 : 2;
0292 idx++;
0293 }
0294 }
0295
0296 static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc,
0297 u8 *pcm_data,
0298 size_t skip,
0299 size_t samples)
0300 {
0301 struct snd_pcm_substream *substream;
0302 struct snd_pcm_runtime *runtime;
0303 unsigned long flags;
0304 unsigned int pos;
0305 unsigned int stride;
0306 bool is_s32;
0307 unsigned i;
0308
0309 dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc,
0310 pcm_data, samples);
0311
0312 substream = cobsc->playback_pcm_substream;
0313 if (substream == NULL) {
0314 dprintk("substream was NULL\n");
0315 return;
0316 }
0317
0318 runtime = substream->runtime;
0319 if (runtime == NULL) {
0320 dprintk("runtime was NULL\n");
0321 return;
0322 }
0323
0324 is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
0325 stride = runtime->frame_bits >> 3;
0326 if (stride == 0) {
0327 dprintk("stride is zero\n");
0328 return;
0329 }
0330
0331 if (samples == 0) {
0332 dprintk("%s: samples was zero\n", __func__);
0333 return;
0334 }
0335
0336 if (runtime->dma_area == NULL) {
0337 dprintk("dma area was NULL - ignoring\n");
0338 return;
0339 }
0340
0341 pos = cobsc->pb_pos % cobsc->pb_size;
0342 for (i = 0; i < cobsc->pb_count / (8 * 4); i++)
0343 pb_sample_cpy(pcm_data + i * skip,
0344 runtime->dma_area + pos + i * stride,
0345 stride, is_s32);
0346 snd_pcm_stream_lock_irqsave(substream, flags);
0347
0348 cobsc->pb_pos += i * stride;
0349
0350 snd_pcm_stream_unlock_irqrestore(substream, flags);
0351 if (cobsc->pb_pos % cobsc->pb_count == 0)
0352 snd_pcm_period_elapsed(substream);
0353 }
0354
0355 static int alsa_pb_fnc(struct vb2_buffer *vb, void *priv)
0356 {
0357 struct cobalt_stream *s = priv;
0358
0359 if (s->alsa->alsa_pb_channel)
0360 cobalt_alsa_pb_pcm_data(s->alsa,
0361 vb2_plane_vaddr(vb, 0),
0362 8 * 4,
0363 vb2_get_plane_payload(vb, 0) / (8 * 4));
0364 return 0;
0365 }
0366
0367 static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream)
0368 {
0369 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
0370 struct snd_pcm_runtime *runtime = substream->runtime;
0371 struct cobalt_stream *s = cobsc->s;
0372
0373 runtime->hw = snd_cobalt_playback;
0374 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
0375 cobsc->playback_pcm_substream = substream;
0376 runtime->private_data = s;
0377 cobsc->alsa_playback_cnt++;
0378 if (cobsc->alsa_playback_cnt == 1) {
0379 int rc;
0380
0381 rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name);
0382 if (rc) {
0383 cobsc->alsa_playback_cnt--;
0384 return rc;
0385 }
0386 }
0387
0388 return 0;
0389 }
0390
0391 static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream)
0392 {
0393 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
0394 struct cobalt_stream *s = cobsc->s;
0395
0396 cobsc->alsa_playback_cnt--;
0397 if (cobsc->alsa_playback_cnt == 0)
0398 vb2_thread_stop(&s->q);
0399 return 0;
0400 }
0401
0402 static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream)
0403 {
0404 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
0405
0406 cobsc->pb_size = snd_pcm_lib_buffer_bytes(substream);
0407 cobsc->pb_count = snd_pcm_lib_period_bytes(substream);
0408 cobsc->pb_pos = 0;
0409
0410 return 0;
0411 }
0412
0413 static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream,
0414 int cmd)
0415 {
0416 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
0417
0418 switch (cmd) {
0419 case SNDRV_PCM_TRIGGER_START:
0420 if (cobsc->alsa_pb_channel)
0421 return -EBUSY;
0422 cobsc->alsa_pb_channel = true;
0423 return 0;
0424 case SNDRV_PCM_TRIGGER_STOP:
0425 cobsc->alsa_pb_channel = false;
0426 return 0;
0427 default:
0428 return -EINVAL;
0429 }
0430 }
0431
0432 static
0433 snd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream)
0434 {
0435 struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
0436 size_t ptr;
0437
0438 ptr = cobsc->pb_pos;
0439
0440 return bytes_to_frames(substream->runtime, ptr) %
0441 substream->runtime->buffer_size;
0442 }
0443
0444 static const struct snd_pcm_ops snd_cobalt_pcm_capture_ops = {
0445 .open = snd_cobalt_pcm_capture_open,
0446 .close = snd_cobalt_pcm_capture_close,
0447 .prepare = snd_cobalt_pcm_prepare,
0448 .trigger = snd_cobalt_pcm_trigger,
0449 .pointer = snd_cobalt_pcm_pointer,
0450 };
0451
0452 static const struct snd_pcm_ops snd_cobalt_pcm_playback_ops = {
0453 .open = snd_cobalt_pcm_playback_open,
0454 .close = snd_cobalt_pcm_playback_close,
0455 .prepare = snd_cobalt_pcm_pb_prepare,
0456 .trigger = snd_cobalt_pcm_pb_trigger,
0457 .pointer = snd_cobalt_pcm_pb_pointer,
0458 };
0459
0460 int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc)
0461 {
0462 struct snd_pcm *sp;
0463 struct snd_card *sc = cobsc->sc;
0464 struct cobalt_stream *s = cobsc->s;
0465 struct cobalt *cobalt = s->cobalt;
0466 int ret;
0467
0468 s->q.gfp_flags |= __GFP_ZERO;
0469
0470 if (!s->is_output) {
0471 cobalt_s_bit_sysctrl(cobalt,
0472 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
0473 0);
0474 mdelay(2);
0475 cobalt_s_bit_sysctrl(cobalt,
0476 COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
0477 1);
0478 mdelay(1);
0479
0480 ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI",
0481 0,
0482 0,
0483 1,
0484 &sp);
0485 if (ret) {
0486 cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n",
0487 ret);
0488 goto err_exit;
0489 }
0490
0491 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
0492 &snd_cobalt_pcm_capture_ops);
0493 snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC,
0494 NULL, 0, 0);
0495 sp->info_flags = 0;
0496 sp->private_data = cobsc;
0497 strscpy(sp->name, "cobalt", sizeof(sp->name));
0498 } else {
0499 cobalt_s_bit_sysctrl(cobalt,
0500 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0);
0501 mdelay(2);
0502 cobalt_s_bit_sysctrl(cobalt,
0503 COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 1);
0504 mdelay(1);
0505
0506 ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI",
0507 0,
0508 1,
0509 0,
0510 &sp);
0511 if (ret) {
0512 cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n",
0513 ret);
0514 goto err_exit;
0515 }
0516
0517 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK,
0518 &snd_cobalt_pcm_playback_ops);
0519 snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC,
0520 NULL, 0, 0);
0521 sp->info_flags = 0;
0522 sp->private_data = cobsc;
0523 strscpy(sp->name, "cobalt", sizeof(sp->name));
0524 }
0525
0526 return 0;
0527
0528 err_exit:
0529 return ret;
0530 }