1 // SPDX-License-Identifier: GPL-2.0+
3 * virtio-snd: Virtio sound device
4 * Copyright (C) 2021 OpenSynergy GmbH
6 #include <linux/virtio_config.h>
8 #include "virtio_card.h"
10 /* VirtIO->ALSA channel position map */
11 static const u8 g_v2a_position_map
[] = {
12 [VIRTIO_SND_CHMAP_NONE
] = SNDRV_CHMAP_UNKNOWN
,
13 [VIRTIO_SND_CHMAP_NA
] = SNDRV_CHMAP_NA
,
14 [VIRTIO_SND_CHMAP_MONO
] = SNDRV_CHMAP_MONO
,
15 [VIRTIO_SND_CHMAP_FL
] = SNDRV_CHMAP_FL
,
16 [VIRTIO_SND_CHMAP_FR
] = SNDRV_CHMAP_FR
,
17 [VIRTIO_SND_CHMAP_RL
] = SNDRV_CHMAP_RL
,
18 [VIRTIO_SND_CHMAP_RR
] = SNDRV_CHMAP_RR
,
19 [VIRTIO_SND_CHMAP_FC
] = SNDRV_CHMAP_FC
,
20 [VIRTIO_SND_CHMAP_LFE
] = SNDRV_CHMAP_LFE
,
21 [VIRTIO_SND_CHMAP_SL
] = SNDRV_CHMAP_SL
,
22 [VIRTIO_SND_CHMAP_SR
] = SNDRV_CHMAP_SR
,
23 [VIRTIO_SND_CHMAP_RC
] = SNDRV_CHMAP_RC
,
24 [VIRTIO_SND_CHMAP_FLC
] = SNDRV_CHMAP_FLC
,
25 [VIRTIO_SND_CHMAP_FRC
] = SNDRV_CHMAP_FRC
,
26 [VIRTIO_SND_CHMAP_RLC
] = SNDRV_CHMAP_RLC
,
27 [VIRTIO_SND_CHMAP_RRC
] = SNDRV_CHMAP_RRC
,
28 [VIRTIO_SND_CHMAP_FLW
] = SNDRV_CHMAP_FLW
,
29 [VIRTIO_SND_CHMAP_FRW
] = SNDRV_CHMAP_FRW
,
30 [VIRTIO_SND_CHMAP_FLH
] = SNDRV_CHMAP_FLH
,
31 [VIRTIO_SND_CHMAP_FCH
] = SNDRV_CHMAP_FCH
,
32 [VIRTIO_SND_CHMAP_FRH
] = SNDRV_CHMAP_FRH
,
33 [VIRTIO_SND_CHMAP_TC
] = SNDRV_CHMAP_TC
,
34 [VIRTIO_SND_CHMAP_TFL
] = SNDRV_CHMAP_TFL
,
35 [VIRTIO_SND_CHMAP_TFR
] = SNDRV_CHMAP_TFR
,
36 [VIRTIO_SND_CHMAP_TFC
] = SNDRV_CHMAP_TFC
,
37 [VIRTIO_SND_CHMAP_TRL
] = SNDRV_CHMAP_TRL
,
38 [VIRTIO_SND_CHMAP_TRR
] = SNDRV_CHMAP_TRR
,
39 [VIRTIO_SND_CHMAP_TRC
] = SNDRV_CHMAP_TRC
,
40 [VIRTIO_SND_CHMAP_TFLC
] = SNDRV_CHMAP_TFLC
,
41 [VIRTIO_SND_CHMAP_TFRC
] = SNDRV_CHMAP_TFRC
,
42 [VIRTIO_SND_CHMAP_TSL
] = SNDRV_CHMAP_TSL
,
43 [VIRTIO_SND_CHMAP_TSR
] = SNDRV_CHMAP_TSR
,
44 [VIRTIO_SND_CHMAP_LLFE
] = SNDRV_CHMAP_LLFE
,
45 [VIRTIO_SND_CHMAP_RLFE
] = SNDRV_CHMAP_RLFE
,
46 [VIRTIO_SND_CHMAP_BC
] = SNDRV_CHMAP_BC
,
47 [VIRTIO_SND_CHMAP_BLC
] = SNDRV_CHMAP_BLC
,
48 [VIRTIO_SND_CHMAP_BRC
] = SNDRV_CHMAP_BRC
52 * virtsnd_chmap_parse_cfg() - Parse the channel map configuration.
53 * @snd: VirtIO sound device.
55 * This function is called during initial device initialization.
57 * Context: Any context that permits to sleep.
58 * Return: 0 on success, -errno on failure.
60 int virtsnd_chmap_parse_cfg(struct virtio_snd
*snd
)
62 struct virtio_device
*vdev
= snd
->vdev
;
66 virtio_cread_le(vdev
, struct virtio_snd_config
, chmaps
, &snd
->nchmaps
);
70 snd
->chmaps
= devm_kcalloc(&vdev
->dev
, snd
->nchmaps
,
71 sizeof(*snd
->chmaps
), GFP_KERNEL
);
75 rc
= virtsnd_ctl_query_info(snd
, VIRTIO_SND_R_CHMAP_INFO
, 0,
76 snd
->nchmaps
, sizeof(*snd
->chmaps
),
81 /* Count the number of channel maps per each PCM device/stream. */
82 for (i
= 0; i
< snd
->nchmaps
; ++i
) {
83 struct virtio_snd_chmap_info
*info
= &snd
->chmaps
[i
];
84 u32 nid
= le32_to_cpu(info
->hdr
.hda_fn_nid
);
85 struct virtio_pcm
*vpcm
;
86 struct virtio_pcm_stream
*vs
;
88 vpcm
= virtsnd_pcm_find_or_create(snd
, nid
);
92 switch (info
->direction
) {
93 case VIRTIO_SND_D_OUTPUT
:
94 vs
= &vpcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
];
96 case VIRTIO_SND_D_INPUT
:
97 vs
= &vpcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
];
101 "chmap #%u: unknown direction (%u)\n", i
,
113 * virtsnd_chmap_add_ctls() - Create an ALSA control for channel maps.
114 * @pcm: ALSA PCM device.
115 * @direction: PCM stream direction (SNDRV_PCM_STREAM_XXX).
116 * @vs: VirtIO PCM stream.
118 * Context: Any context.
119 * Return: 0 on success, -errno on failure.
121 static int virtsnd_chmap_add_ctls(struct snd_pcm
*pcm
, int direction
,
122 struct virtio_pcm_stream
*vs
)
125 int max_channels
= 0;
127 for (i
= 0; i
< vs
->nchmaps
; i
++)
128 if (max_channels
< vs
->chmaps
[i
].channels
)
129 max_channels
= vs
->chmaps
[i
].channels
;
131 return snd_pcm_add_chmap_ctls(pcm
, direction
, vs
->chmaps
, max_channels
,
136 * virtsnd_chmap_build_devs() - Build ALSA controls for channel maps.
137 * @snd: VirtIO sound device.
139 * Context: Any context.
140 * Return: 0 on success, -errno on failure.
142 int virtsnd_chmap_build_devs(struct virtio_snd
*snd
)
144 struct virtio_device
*vdev
= snd
->vdev
;
145 struct virtio_pcm
*vpcm
;
146 struct virtio_pcm_stream
*vs
;
150 /* Allocate channel map elements per each PCM device/stream. */
151 list_for_each_entry(vpcm
, &snd
->pcm_list
, list
) {
152 for (i
= 0; i
< ARRAY_SIZE(vpcm
->streams
); ++i
) {
153 vs
= &vpcm
->streams
[i
];
158 vs
->chmaps
= devm_kcalloc(&vdev
->dev
, vs
->nchmaps
+ 1,
168 /* Initialize channel maps per each PCM device/stream. */
169 for (i
= 0; i
< snd
->nchmaps
; ++i
) {
170 struct virtio_snd_chmap_info
*info
= &snd
->chmaps
[i
];
171 unsigned int channels
= info
->channels
;
173 struct snd_pcm_chmap_elem
*chmap
;
175 vpcm
= virtsnd_pcm_find(snd
, le32_to_cpu(info
->hdr
.hda_fn_nid
));
177 return PTR_ERR(vpcm
);
179 if (info
->direction
== VIRTIO_SND_D_OUTPUT
)
180 vs
= &vpcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
];
182 vs
= &vpcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
];
184 chmap
= &vs
->chmaps
[vs
->nchmaps
++];
186 if (channels
> ARRAY_SIZE(chmap
->map
))
187 channels
= ARRAY_SIZE(chmap
->map
);
189 chmap
->channels
= channels
;
191 for (ch
= 0; ch
< channels
; ++ch
) {
192 u8 position
= info
->positions
[ch
];
194 if (position
>= ARRAY_SIZE(g_v2a_position_map
))
197 chmap
->map
[ch
] = g_v2a_position_map
[position
];
201 /* Create an ALSA control per each PCM device/stream. */
202 list_for_each_entry(vpcm
, &snd
->pcm_list
, list
) {
206 for (i
= 0; i
< ARRAY_SIZE(vpcm
->streams
); ++i
) {
207 vs
= &vpcm
->streams
[i
];
212 rc
= virtsnd_chmap_add_ctls(vpcm
->pcm
, i
, vs
);