dice-pcm.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. * dice_pcm.c - a part of driver for DICE based devices
  3. *
  4. * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  5. * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
  6. *
  7. * Licensed under the terms of the GNU General Public License, version 2.
  8. */
  9. #include "dice.h"
  10. static int dice_rate_constraint(struct snd_pcm_hw_params *params,
  11. struct snd_pcm_hw_rule *rule)
  12. {
  13. struct snd_dice *dice = rule->private;
  14. const struct snd_interval *c =
  15. hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
  16. struct snd_interval *r =
  17. hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
  18. struct snd_interval rates = {
  19. .min = UINT_MAX, .max = 0, .integer = 1
  20. };
  21. unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
  22. for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
  23. rate = snd_dice_rates[i];
  24. if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
  25. continue;
  26. if (!snd_interval_test(c, pcm_channels[mode]))
  27. continue;
  28. rates.min = min(rates.min, rate);
  29. rates.max = max(rates.max, rate);
  30. }
  31. return snd_interval_refine(r, &rates);
  32. }
  33. static int dice_channels_constraint(struct snd_pcm_hw_params *params,
  34. struct snd_pcm_hw_rule *rule)
  35. {
  36. struct snd_dice *dice = rule->private;
  37. const struct snd_interval *r =
  38. hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
  39. struct snd_interval *c =
  40. hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
  41. struct snd_interval channels = {
  42. .min = UINT_MAX, .max = 0, .integer = 1
  43. };
  44. unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
  45. for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
  46. rate = snd_dice_rates[i];
  47. if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
  48. continue;
  49. if (!snd_interval_test(r, rate))
  50. continue;
  51. channels.min = min(channels.min, pcm_channels[mode]);
  52. channels.max = max(channels.max, pcm_channels[mode]);
  53. }
  54. return snd_interval_refine(c, &channels);
  55. }
  56. static void limit_channels_and_rates(struct snd_dice *dice,
  57. struct snd_pcm_runtime *runtime,
  58. unsigned int *pcm_channels)
  59. {
  60. struct snd_pcm_hardware *hw = &runtime->hw;
  61. unsigned int i, rate, mode;
  62. hw->channels_min = UINT_MAX;
  63. hw->channels_max = 0;
  64. for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
  65. rate = snd_dice_rates[i];
  66. if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
  67. continue;
  68. hw->rates |= snd_pcm_rate_to_rate_bit(rate);
  69. if (pcm_channels[mode] == 0)
  70. continue;
  71. hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
  72. hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
  73. }
  74. snd_pcm_limit_hw_rates(runtime);
  75. }
  76. static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
  77. {
  78. hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
  79. hw->periods_max = UINT_MAX;
  80. hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */
  81. /* Just to prevent from allocating much pages. */
  82. hw->period_bytes_max = hw->period_bytes_min * 2048;
  83. hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
  84. }
  85. static int init_hw_info(struct snd_dice *dice,
  86. struct snd_pcm_substream *substream)
  87. {
  88. struct snd_pcm_runtime *runtime = substream->runtime;
  89. struct snd_pcm_hardware *hw = &runtime->hw;
  90. int err;
  91. hw->info = SNDRV_PCM_INFO_MMAP |
  92. SNDRV_PCM_INFO_MMAP_VALID |
  93. SNDRV_PCM_INFO_BATCH |
  94. SNDRV_PCM_INFO_INTERLEAVED |
  95. SNDRV_PCM_INFO_BLOCK_TRANSFER;
  96. hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
  97. limit_channels_and_rates(dice, runtime, dice->rx_channels);
  98. limit_period_and_buffer(hw);
  99. err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
  100. dice_rate_constraint, dice,
  101. SNDRV_PCM_HW_PARAM_CHANNELS, -1);
  102. if (err < 0)
  103. goto end;
  104. err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
  105. dice_channels_constraint, dice,
  106. SNDRV_PCM_HW_PARAM_RATE, -1);
  107. if (err < 0)
  108. goto end;
  109. err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime);
  110. end:
  111. return err;
  112. }
  113. static int pcm_open(struct snd_pcm_substream *substream)
  114. {
  115. struct snd_dice *dice = substream->private_data;
  116. int err;
  117. err = snd_dice_stream_lock_try(dice);
  118. if (err < 0)
  119. goto end;
  120. err = init_hw_info(dice, substream);
  121. if (err < 0)
  122. goto err_locked;
  123. end:
  124. return err;
  125. err_locked:
  126. snd_dice_stream_lock_release(dice);
  127. return err;
  128. }
  129. static int pcm_close(struct snd_pcm_substream *substream)
  130. {
  131. struct snd_dice *dice = substream->private_data;
  132. snd_dice_stream_lock_release(dice);
  133. return 0;
  134. }
  135. static int playback_hw_params(struct snd_pcm_substream *substream,
  136. struct snd_pcm_hw_params *hw_params)
  137. {
  138. struct snd_dice *dice = substream->private_data;
  139. amdtp_stream_set_pcm_format(&dice->rx_stream,
  140. params_format(hw_params));
  141. return snd_pcm_lib_alloc_vmalloc_buffer(substream,
  142. params_buffer_bytes(hw_params));
  143. }
  144. static int playback_hw_free(struct snd_pcm_substream *substream)
  145. {
  146. struct snd_dice *dice = substream->private_data;
  147. mutex_lock(&dice->mutex);
  148. snd_dice_stream_stop_duplex(dice);
  149. mutex_unlock(&dice->mutex);
  150. return snd_pcm_lib_free_vmalloc_buffer(substream);
  151. }
  152. static int playback_prepare(struct snd_pcm_substream *substream)
  153. {
  154. struct snd_dice *dice = substream->private_data;
  155. int err;
  156. mutex_lock(&dice->mutex);
  157. err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
  158. mutex_unlock(&dice->mutex);
  159. if (err >= 0)
  160. amdtp_stream_pcm_prepare(&dice->rx_stream);
  161. return err;
  162. }
  163. static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
  164. {
  165. struct snd_dice *dice = substream->private_data;
  166. switch (cmd) {
  167. case SNDRV_PCM_TRIGGER_START:
  168. amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
  169. break;
  170. case SNDRV_PCM_TRIGGER_STOP:
  171. amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
  172. break;
  173. default:
  174. return -EINVAL;
  175. }
  176. return 0;
  177. }
  178. static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
  179. {
  180. struct snd_dice *dice = substream->private_data;
  181. return amdtp_stream_pcm_pointer(&dice->rx_stream);
  182. }
  183. int snd_dice_create_pcm(struct snd_dice *dice)
  184. {
  185. static struct snd_pcm_ops playback_ops = {
  186. .open = pcm_open,
  187. .close = pcm_close,
  188. .ioctl = snd_pcm_lib_ioctl,
  189. .hw_params = playback_hw_params,
  190. .hw_free = playback_hw_free,
  191. .prepare = playback_prepare,
  192. .trigger = playback_trigger,
  193. .pointer = playback_pointer,
  194. .page = snd_pcm_lib_get_vmalloc_page,
  195. .mmap = snd_pcm_lib_mmap_vmalloc,
  196. };
  197. struct snd_pcm *pcm;
  198. int err;
  199. err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
  200. if (err < 0)
  201. return err;
  202. pcm->private_data = dice;
  203. strcpy(pcm->name, dice->card->shortname);
  204. snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
  205. return 0;
  206. }