477 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.4 *5 * @File ctpcm.c6 *7 * @Brief8 * This file contains the definition of the pcm device functions.9 *10 * @Author Liu Chun11 * @Date Apr 2 200812 */13 14#include "ctpcm.h"15#include "cttimer.h"16#include <linux/slab.h>17#include <sound/pcm.h>18 19/* Hardware descriptions for playback */20static const struct snd_pcm_hardware ct_pcm_playback_hw = {21 .info = (SNDRV_PCM_INFO_MMAP |22 SNDRV_PCM_INFO_INTERLEAVED |23 SNDRV_PCM_INFO_BLOCK_TRANSFER |24 SNDRV_PCM_INFO_MMAP_VALID |25 SNDRV_PCM_INFO_PAUSE),26 .formats = (SNDRV_PCM_FMTBIT_U8 |27 SNDRV_PCM_FMTBIT_S16_LE |28 SNDRV_PCM_FMTBIT_S24_3LE |29 SNDRV_PCM_FMTBIT_S32_LE |30 SNDRV_PCM_FMTBIT_FLOAT_LE),31 .rates = (SNDRV_PCM_RATE_CONTINUOUS |32 SNDRV_PCM_RATE_8000_192000),33 .rate_min = 8000,34 .rate_max = 192000,35 .channels_min = 1,36 .channels_max = 2,37 .buffer_bytes_max = (128*1024),38 .period_bytes_min = (64),39 .period_bytes_max = (128*1024),40 .periods_min = 2,41 .periods_max = 1024,42 .fifo_size = 0,43};44 45static const struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {46 .info = (SNDRV_PCM_INFO_MMAP |47 SNDRV_PCM_INFO_INTERLEAVED |48 SNDRV_PCM_INFO_BLOCK_TRANSFER |49 SNDRV_PCM_INFO_MMAP_VALID |50 SNDRV_PCM_INFO_PAUSE),51 .formats = SNDRV_PCM_FMTBIT_S16_LE,52 .rates = (SNDRV_PCM_RATE_48000 |53 SNDRV_PCM_RATE_44100 |54 SNDRV_PCM_RATE_32000),55 .rate_min = 32000,56 .rate_max = 48000,57 .channels_min = 2,58 .channels_max = 2,59 .buffer_bytes_max = (128*1024),60 .period_bytes_min = (64),61 .period_bytes_max = (128*1024),62 .periods_min = 2,63 .periods_max = 1024,64 .fifo_size = 0,65};66 67/* Hardware descriptions for capture */68static const struct snd_pcm_hardware ct_pcm_capture_hw = {69 .info = (SNDRV_PCM_INFO_MMAP |70 SNDRV_PCM_INFO_INTERLEAVED |71 SNDRV_PCM_INFO_BLOCK_TRANSFER |72 SNDRV_PCM_INFO_PAUSE |73 SNDRV_PCM_INFO_MMAP_VALID),74 .formats = (SNDRV_PCM_FMTBIT_U8 |75 SNDRV_PCM_FMTBIT_S16_LE |76 SNDRV_PCM_FMTBIT_S24_3LE |77 SNDRV_PCM_FMTBIT_S32_LE |78 SNDRV_PCM_FMTBIT_FLOAT_LE),79 .rates = (SNDRV_PCM_RATE_CONTINUOUS |80 SNDRV_PCM_RATE_8000_96000),81 .rate_min = 8000,82 .rate_max = 96000,83 .channels_min = 1,84 .channels_max = 2,85 .buffer_bytes_max = (128*1024),86 .period_bytes_min = (384),87 .period_bytes_max = (64*1024),88 .periods_min = 2,89 .periods_max = 1024,90 .fifo_size = 0,91};92 93static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)94{95 struct ct_atc_pcm *apcm = atc_pcm;96 97 if (!apcm->substream)98 return;99 100 snd_pcm_period_elapsed(apcm->substream);101}102 103static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)104{105 struct ct_atc_pcm *apcm = runtime->private_data;106 struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);107 108 atc->pcm_release_resources(atc, apcm);109 ct_timer_instance_free(apcm->timer);110 kfree(apcm);111 runtime->private_data = NULL;112}113 114/* pcm playback operations */115static int ct_pcm_playback_open(struct snd_pcm_substream *substream)116{117 struct ct_atc *atc = snd_pcm_substream_chip(substream);118 struct snd_pcm_runtime *runtime = substream->runtime;119 struct ct_atc_pcm *apcm;120 int err;121 122 apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);123 if (!apcm)124 return -ENOMEM;125 126 apcm->substream = substream;127 apcm->interrupt = ct_atc_pcm_interrupt;128 if (IEC958 == substream->pcm->device) {129 runtime->hw = ct_spdif_passthru_playback_hw;130 atc->spdif_out_passthru(atc, 1);131 } else {132 runtime->hw = ct_pcm_playback_hw;133 if (FRONT == substream->pcm->device)134 runtime->hw.channels_max = 8;135 }136 137 err = snd_pcm_hw_constraint_integer(runtime,138 SNDRV_PCM_HW_PARAM_PERIODS);139 if (err < 0)140 goto free_pcm;141 142 err = snd_pcm_hw_constraint_minmax(runtime,143 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,144 1024, UINT_MAX);145 if (err < 0)146 goto free_pcm;147 148 apcm->timer = ct_timer_instance_new(atc->timer, apcm);149 if (!apcm->timer) {150 err = -ENOMEM;151 goto free_pcm;152 }153 runtime->private_data = apcm;154 runtime->private_free = ct_atc_pcm_free_substream;155 156 return 0;157 158free_pcm:159 kfree(apcm);160 return err;161}162 163static int ct_pcm_playback_close(struct snd_pcm_substream *substream)164{165 struct ct_atc *atc = snd_pcm_substream_chip(substream);166 167 /* TODO: Notify mixer inactive. */168 if (IEC958 == substream->pcm->device)169 atc->spdif_out_passthru(atc, 0);170 171 /* The ct_atc_pcm object will be freed by runtime->private_free */172 173 return 0;174}175 176static int ct_pcm_hw_params(struct snd_pcm_substream *substream,177 struct snd_pcm_hw_params *hw_params)178{179 struct ct_atc *atc = snd_pcm_substream_chip(substream);180 struct ct_atc_pcm *apcm = substream->runtime->private_data;181 182 /* clear previous resources */183 atc->pcm_release_resources(atc, apcm);184 return 0;185}186 187static int ct_pcm_hw_free(struct snd_pcm_substream *substream)188{189 struct ct_atc *atc = snd_pcm_substream_chip(substream);190 struct ct_atc_pcm *apcm = substream->runtime->private_data;191 192 /* clear previous resources */193 atc->pcm_release_resources(atc, apcm);194 return 0;195}196 197 198static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)199{200 int err;201 struct ct_atc *atc = snd_pcm_substream_chip(substream);202 struct snd_pcm_runtime *runtime = substream->runtime;203 struct ct_atc_pcm *apcm = runtime->private_data;204 205 if (IEC958 == substream->pcm->device)206 err = atc->spdif_passthru_playback_prepare(atc, apcm);207 else208 err = atc->pcm_playback_prepare(atc, apcm);209 210 if (err < 0) {211 dev_err(atc->card->dev,212 "Preparing pcm playback failed!!!\n");213 return err;214 }215 216 return 0;217}218 219static int220ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)221{222 struct ct_atc *atc = snd_pcm_substream_chip(substream);223 struct snd_pcm_runtime *runtime = substream->runtime;224 struct ct_atc_pcm *apcm = runtime->private_data;225 226 switch (cmd) {227 case SNDRV_PCM_TRIGGER_START:228 case SNDRV_PCM_TRIGGER_RESUME:229 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:230 atc->pcm_playback_start(atc, apcm);231 break;232 case SNDRV_PCM_TRIGGER_STOP:233 case SNDRV_PCM_TRIGGER_SUSPEND:234 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:235 atc->pcm_playback_stop(atc, apcm);236 break;237 default:238 break;239 }240 241 return 0;242}243 244static snd_pcm_uframes_t245ct_pcm_playback_pointer(struct snd_pcm_substream *substream)246{247 unsigned long position;248 struct ct_atc *atc = snd_pcm_substream_chip(substream);249 struct snd_pcm_runtime *runtime = substream->runtime;250 struct ct_atc_pcm *apcm = runtime->private_data;251 252 /* Read out playback position */253 position = atc->pcm_playback_position(atc, apcm);254 position = bytes_to_frames(runtime, position);255 if (position >= runtime->buffer_size)256 position = 0;257 return position;258}259 260/* pcm capture operations */261static int ct_pcm_capture_open(struct snd_pcm_substream *substream)262{263 struct ct_atc *atc = snd_pcm_substream_chip(substream);264 struct snd_pcm_runtime *runtime = substream->runtime;265 struct ct_atc_pcm *apcm;266 int err;267 268 apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);269 if (!apcm)270 return -ENOMEM;271 272 apcm->started = 0;273 apcm->substream = substream;274 apcm->interrupt = ct_atc_pcm_interrupt;275 runtime->hw = ct_pcm_capture_hw;276 runtime->hw.rate_max = atc->rsr * atc->msr;277 278 err = snd_pcm_hw_constraint_integer(runtime,279 SNDRV_PCM_HW_PARAM_PERIODS);280 if (err < 0)281 goto free_pcm;282 283 err = snd_pcm_hw_constraint_minmax(runtime,284 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,285 1024, UINT_MAX);286 if (err < 0)287 goto free_pcm;288 289 apcm->timer = ct_timer_instance_new(atc->timer, apcm);290 if (!apcm->timer) {291 err = -ENOMEM;292 goto free_pcm;293 }294 runtime->private_data = apcm;295 runtime->private_free = ct_atc_pcm_free_substream;296 297 return 0;298 299free_pcm:300 kfree(apcm);301 return err;302}303 304static int ct_pcm_capture_close(struct snd_pcm_substream *substream)305{306 /* The ct_atc_pcm object will be freed by runtime->private_free */307 /* TODO: Notify mixer inactive. */308 return 0;309}310 311static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)312{313 int err;314 struct ct_atc *atc = snd_pcm_substream_chip(substream);315 struct snd_pcm_runtime *runtime = substream->runtime;316 struct ct_atc_pcm *apcm = runtime->private_data;317 318 err = atc->pcm_capture_prepare(atc, apcm);319 if (err < 0) {320 dev_err(atc->card->dev,321 "Preparing pcm capture failed!!!\n");322 return err;323 }324 325 return 0;326}327 328static int329ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)330{331 struct ct_atc *atc = snd_pcm_substream_chip(substream);332 struct snd_pcm_runtime *runtime = substream->runtime;333 struct ct_atc_pcm *apcm = runtime->private_data;334 335 switch (cmd) {336 case SNDRV_PCM_TRIGGER_START:337 atc->pcm_capture_start(atc, apcm);338 break;339 case SNDRV_PCM_TRIGGER_STOP:340 atc->pcm_capture_stop(atc, apcm);341 break;342 default:343 atc->pcm_capture_stop(atc, apcm);344 break;345 }346 347 return 0;348}349 350static snd_pcm_uframes_t351ct_pcm_capture_pointer(struct snd_pcm_substream *substream)352{353 unsigned long position;354 struct ct_atc *atc = snd_pcm_substream_chip(substream);355 struct snd_pcm_runtime *runtime = substream->runtime;356 struct ct_atc_pcm *apcm = runtime->private_data;357 358 /* Read out playback position */359 position = atc->pcm_capture_position(atc, apcm);360 position = bytes_to_frames(runtime, position);361 if (position >= runtime->buffer_size)362 position = 0;363 return position;364}365 366/* PCM operators for playback */367static const struct snd_pcm_ops ct_pcm_playback_ops = {368 .open = ct_pcm_playback_open,369 .close = ct_pcm_playback_close,370 .hw_params = ct_pcm_hw_params,371 .hw_free = ct_pcm_hw_free,372 .prepare = ct_pcm_playback_prepare,373 .trigger = ct_pcm_playback_trigger,374 .pointer = ct_pcm_playback_pointer,375};376 377/* PCM operators for capture */378static const struct snd_pcm_ops ct_pcm_capture_ops = {379 .open = ct_pcm_capture_open,380 .close = ct_pcm_capture_close,381 .hw_params = ct_pcm_hw_params,382 .hw_free = ct_pcm_hw_free,383 .prepare = ct_pcm_capture_prepare,384 .trigger = ct_pcm_capture_trigger,385 .pointer = ct_pcm_capture_pointer,386};387 388static const struct snd_pcm_chmap_elem surround_map[] = {389 { .channels = 1,390 .map = { SNDRV_CHMAP_MONO } },391 { .channels = 2,392 .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },393 { }394};395 396static const struct snd_pcm_chmap_elem clfe_map[] = {397 { .channels = 1,398 .map = { SNDRV_CHMAP_MONO } },399 { .channels = 2,400 .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },401 { }402};403 404static const struct snd_pcm_chmap_elem side_map[] = {405 { .channels = 1,406 .map = { SNDRV_CHMAP_MONO } },407 { .channels = 2,408 .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },409 { }410};411 412/* Create ALSA pcm device */413int ct_alsa_pcm_create(struct ct_atc *atc,414 enum CTALSADEVS device,415 const char *device_name)416{417 struct snd_pcm *pcm;418 const struct snd_pcm_chmap_elem *map;419 int chs;420 int err;421 int playback_count, capture_count;422 423 playback_count = (IEC958 == device) ? 1 : 256;424 capture_count = (FRONT == device) ? 1 : 0;425 err = snd_pcm_new(atc->card, "ctxfi", device,426 playback_count, capture_count, &pcm);427 if (err < 0) {428 dev_err(atc->card->dev, "snd_pcm_new failed!! Err=%d\n",429 err);430 return err;431 }432 433 pcm->private_data = atc;434 pcm->info_flags = 0;435 pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;436 strscpy(pcm->name, device_name, sizeof(pcm->name));437 438 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);439 440 if (FRONT == device)441 snd_pcm_set_ops(pcm,442 SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);443 444 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,445 &atc->pci->dev, 128*1024, 128*1024);446 447 chs = 2;448 switch (device) {449 case FRONT:450 chs = 8;451 map = snd_pcm_std_chmaps;452 break;453 case SURROUND:454 map = surround_map;455 break;456 case CLFE:457 map = clfe_map;458 break;459 case SIDE:460 map = side_map;461 break;462 default:463 map = snd_pcm_std_chmaps;464 break;465 }466 err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs,467 0, NULL);468 if (err < 0)469 return err;470 471#ifdef CONFIG_PM_SLEEP472 atc->pcms[device] = pcm;473#endif474 475 return 0;476}477