1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <fbl/auto_call.h>
6
7 #include <zircon/device/audio.h>
8
9 #include <audio-proto-utils/format-utils.h>
10
11 #include <utility>
12
13 #include "intel-audio-dsp.h"
14 #include "intel-dsp-stream.h"
15
16 namespace audio {
17 namespace intel_hda {
18
IntelDspStream(uint32_t id,bool is_input,const DspPipeline & pipeline,const audio_stream_unique_id_t * unique_id)19 IntelDspStream::IntelDspStream(uint32_t id,
20 bool is_input,
21 const DspPipeline& pipeline,
22 const audio_stream_unique_id_t* unique_id)
23 : IntelHDAStreamBase(id, is_input), pipeline_(pipeline) {
24 snprintf(log_prefix_, sizeof(log_prefix_), "IHDA DSP %cStream #%u", is_input ? 'I' : 'O', id);
25
26 if (unique_id) {
27 SetPersistentUniqueId(*unique_id);
28 } else {
29 const audio_stream_unique_id_t uid = {
30 'I', 'D', 'S', 'P',
31 static_cast<uint8_t>(id >> 24),
32 static_cast<uint8_t>(id >> 16),
33 static_cast<uint8_t>(id >> 8),
34 static_cast<uint8_t>(id),
35 static_cast<uint8_t>(is_input),
36 0
37 };
38 SetPersistentUniqueId(uid);
39 }
40 }
41
ProcessSetStreamFmt(const ihda_proto::SetStreamFmtResp & codec_resp,zx::channel && ring_buffer_channel)42 zx_status_t IntelDspStream::ProcessSetStreamFmt(const ihda_proto::SetStreamFmtResp& codec_resp,
43 zx::channel&& ring_buffer_channel) {
44 ZX_DEBUG_ASSERT(ring_buffer_channel.is_valid());
45
46 fbl::AutoLock lock(obj_lock());
47 audio_proto::StreamSetFmtResp resp = { };
48 zx_status_t res = ZX_OK;
49
50 // Are we shutting down?
51 if (!is_active()) {
52 return ZX_ERR_BAD_STATE;
53 }
54
55 // The DSP needs to coordinate with ring buffer commands. Set up an additional
56 // channel to intercept messages on the ring buffer channel.
57 zx::channel client_endpoint;
58 res = CreateClientRingBufferChannelLocked(std::move(ring_buffer_channel), &client_endpoint);
59 if (res != ZX_OK) {
60 LOG(ERROR, "Failed to set up client ring buffer channel (res %d)\n", res);
61 goto finished;
62 }
63
64 // Let the implementation send the commands required to finish changing the
65 // stream format.
66 res = FinishChangeStreamFormatLocked(encoded_fmt());
67 if (res != ZX_OK) {
68 LOG(ERROR, "Failed to finish set format (enc fmt 0x%04hx res %d)\n", encoded_fmt(), res);
69 goto finished;
70 }
71
72 ZX_DEBUG_ASSERT(client_endpoint.is_valid());
73
74 // Respond to the caller, transferring the DMA handle back in the process.
75 resp.hdr.cmd = AUDIO_STREAM_CMD_SET_FORMAT;
76 resp.hdr.transaction_id = set_format_tid();
77 resp.result = ZX_OK;
78 resp.external_delay_nsec = 0; // report his properly based on the codec path delay.
79 res = stream_channel()->Write(&resp, sizeof(resp), std::move(client_endpoint));
80
81 // If we don't have a set format operation in flight, or the stream channel
82 // has been closed, this set format operation has been canceled. Do not
83 // return an error up the stack; we don't want to close the connection to
84 // our codec device.
85 if ((set_format_tid() == AUDIO_INVALID_TRANSACTION_ID) ||
86 (stream_channel() == nullptr)) {
87 goto finished;
88 }
89
90 finished:
91 // Something went fatally wrong when trying to send the result back to the
92 // caller. Close the stream channel.
93 if ((res != ZX_OK) && (stream_channel() != nullptr)) {
94 OnChannelDeactivateLocked(*stream_channel());
95 stream_channel()->Deactivate();
96 stream_channel() = nullptr;
97 }
98
99 // One way or the other, this set format operation is finished. Clear out
100 // the in-flight transaction ID
101 SetFormatTidLocked(AUDIO_INVALID_TRANSACTION_ID);
102
103 return ZX_OK;
104 }
105
CreateClientRingBufferChannelLocked(zx::channel && ring_buffer_channel,zx::channel * out_client_channel)106 zx_status_t IntelDspStream::CreateClientRingBufferChannelLocked(
107 zx::channel&& ring_buffer_channel,
108 zx::channel* out_client_channel) {
109 // Attempt to allocate a new ring buffer channel and bind it to us.
110 // This channel is connected to the upstream device.
111 auto channel = dispatcher::Channel::Create();
112 if (channel == nullptr) {
113 return ZX_ERR_NO_MEMORY;
114 }
115
116 dispatcher::Channel::ProcessHandler phandler(
117 [stream = fbl::WrapRefPtr(this)](dispatcher::Channel* channel) -> zx_status_t {
118 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
119 return stream->ProcessRbRequest(channel);
120 });
121
122 dispatcher::Channel::ChannelClosedHandler chandler(
123 [stream = fbl::WrapRefPtr(this)](const dispatcher::Channel* channel) -> void {
124 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
125 stream->ProcessRbDeactivate(channel);
126 });
127
128 zx_status_t res = channel->Activate(std::move(ring_buffer_channel),
129 domain(),
130 std::move(phandler),
131 std::move(chandler));
132 if (res != ZX_OK) {
133 return res;
134 }
135 ZX_DEBUG_ASSERT(rb_channel_ == nullptr);
136 rb_channel_ = channel;
137
138 // Attempt to allocate a new ring buffer channel and bind it to us.
139 // This channel is connected to the client.
140 auto client_channel = dispatcher::Channel::Create();
141 if (client_channel == nullptr) {
142 rb_channel_->Deactivate();
143 rb_channel_ = nullptr;
144 return ZX_ERR_NO_MEMORY;
145 }
146
147 dispatcher::Channel::ProcessHandler client_phandler(
148 [stream = fbl::WrapRefPtr(this)](dispatcher::Channel* channel) -> zx_status_t {
149 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
150 return stream->ProcessClientRbRequest(channel);
151 });
152
153 dispatcher::Channel::ChannelClosedHandler client_chandler(
154 [stream = fbl::WrapRefPtr(this)](const dispatcher::Channel* channel) -> void {
155 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
156 stream->ProcessClientRbDeactivate(channel);
157 });
158
159 res = client_channel->Activate(out_client_channel,
160 domain(),
161 std::move(client_phandler),
162 std::move(client_chandler));
163 if (res == ZX_OK) {
164 ZX_DEBUG_ASSERT(client_rb_channel_ == nullptr);
165 client_rb_channel_ = client_channel;
166 } else {
167 rb_channel_->Deactivate();
168 rb_channel_ = nullptr;
169 }
170
171 return res;
172 }
173
ProcessRbRequest(dispatcher::Channel * channel)174 zx_status_t IntelDspStream::ProcessRbRequest(dispatcher::Channel* channel) {
175 ZX_DEBUG_ASSERT(channel != nullptr);
176 fbl::AutoLock lock(obj_lock());
177
178 // If we have lost our connection to the codec device, or are in the process
179 // of shutting down, there is nothing further we can do. Fail the request
180 // and close the connection to the caller.
181 if (!is_active() || (rb_channel_ == nullptr) || (client_rb_channel_ == nullptr)) {
182 return ZX_ERR_BAD_STATE;
183 }
184
185 zx::handle rxed_handle;
186 uint32_t req_size;
187 union {
188 audio_proto::CmdHdr hdr;
189 audio_proto::RingBufGetFifoDepthResp get_fifo_depth;
190 audio_proto::RingBufGetBufferResp get_buffer;
191 audio_proto::RingBufStartResp start;
192 audio_proto::RingBufStopResp stop;
193 } req;
194 // TODO(johngro) : How large is too large?
195 static_assert(sizeof(req) <= 256, "Request buffer is too large to hold on the stack!");
196
197 zx_status_t res = channel->Read(&req, sizeof(req), &req_size, &rxed_handle);
198 if (res != ZX_OK) {
199 return res;
200 }
201
202 switch (req.hdr.cmd) {
203 case AUDIO_RB_CMD_START:
204 {
205 auto dsp = fbl::RefPtr<IntelAudioDsp>::Downcast(parent_codec());
206 zx_status_t st = dsp->StartPipeline(pipeline_);
207 if (st != ZX_OK) {
208 audio_proto::RingBufStartResp resp = { };
209 resp.hdr = req.hdr;
210 resp.result = st;
211 return client_rb_channel_->Write(&resp, sizeof(resp));
212 }
213 break;
214 }
215 default:
216 break;
217 }
218
219 return client_rb_channel_->Write(&req, req_size, std::move(rxed_handle));
220 }
221
ProcessRbDeactivate(const dispatcher::Channel * channel)222 void IntelDspStream::ProcessRbDeactivate(const dispatcher::Channel* channel) {
223 ZX_DEBUG_ASSERT(channel != nullptr);
224 fbl::AutoLock lock(obj_lock());
225
226 LOG(TRACE, "ProcessClientRbDeactivate\n");
227
228 ZX_DEBUG_ASSERT(channel == rb_channel_.get());
229 rb_channel_->Deactivate();
230 rb_channel_ = nullptr;
231
232 // Deactivate the client channel.
233 if (client_rb_channel_ != nullptr) {
234 client_rb_channel_->Deactivate();
235 client_rb_channel_ = nullptr;
236 }
237 }
238
ProcessClientRbRequest(dispatcher::Channel * channel)239 zx_status_t IntelDspStream::ProcessClientRbRequest(dispatcher::Channel* channel) {
240 ZX_DEBUG_ASSERT(channel != nullptr);
241 fbl::AutoLock lock(obj_lock());
242
243 // If we have lost our connection to the codec device, or are in the process
244 // of shutting down, there is nothing further we can do. Fail the request
245 // and close the connection to the caller.
246 if (!is_active() || (rb_channel_ == nullptr) || (client_rb_channel_ == nullptr)) {
247 return ZX_ERR_BAD_STATE;
248 }
249
250 uint32_t req_size;
251 union {
252 audio_proto::CmdHdr hdr;
253 audio_proto::RingBufGetFifoDepthReq get_fifo_depth;
254 audio_proto::RingBufGetBufferReq get_buffer;
255 audio_proto::RingBufStartReq start;
256 audio_proto::RingBufStopReq stop;
257 } req;
258 // TODO(johngro) : How large is too large?
259 static_assert(sizeof(req) <= 256, "Request buffer is too large to hold on the stack!");
260
261 zx_status_t res = channel->Read(&req, sizeof(req), &req_size);
262 if (res != ZX_OK) {
263 return res;
264 }
265
266 switch (req.hdr.cmd) {
267 case AUDIO_RB_CMD_STOP:
268 {
269 auto dsp = fbl::RefPtr<IntelAudioDsp>::Downcast(parent_codec());
270 zx_status_t st = dsp->PausePipeline(pipeline_);
271 if (st != ZX_OK) {
272 audio_proto::RingBufStopResp resp = { };
273 resp.hdr = req.hdr;
274 resp.result = st;
275 return channel->Write(&resp, sizeof(resp));
276 }
277 break;
278 }
279 default:
280 break;
281 }
282
283 return rb_channel_->Write(&req, req_size);
284 }
285
ProcessClientRbDeactivate(const dispatcher::Channel * channel)286 void IntelDspStream::ProcessClientRbDeactivate(const dispatcher::Channel* channel) {
287 ZX_DEBUG_ASSERT(channel != nullptr);
288 fbl::AutoLock lock(obj_lock());
289
290 LOG(TRACE, "ProcessClientRbDeactivate\n");
291
292 ZX_DEBUG_ASSERT(channel == client_rb_channel_.get());
293 client_rb_channel_->Deactivate();
294 client_rb_channel_ = nullptr;
295
296 // Deactivate the upstream channel.
297 if (rb_channel_ != nullptr) {
298 rb_channel_->Deactivate();
299 rb_channel_ = nullptr;
300 }
301 }
302
OnActivateLocked()303 zx_status_t IntelDspStream::OnActivateLocked() {
304 // FIXME(yky) Hardcode supported formats.
305 audio_stream_format_range_t fmt;
306 fmt.sample_formats = AUDIO_SAMPLE_FORMAT_16BIT;
307 fmt.min_frames_per_second =
308 fmt.max_frames_per_second = 48000;
309 fmt.min_channels =
310 fmt.max_channels = 2;
311 fmt.flags = ASF_RANGE_FLAG_FPS_48000_FAMILY;
312
313 fbl::Vector<audio_proto::FormatRange> supported_formats;
314 fbl::AllocChecker ac;
315 supported_formats.push_back(fmt, &ac);
316 if (!ac.check()) {
317 return ZX_ERR_NO_MEMORY;
318 }
319
320 SetSupportedFormatsLocked(std::move(supported_formats));
321 return ZX_OK;
322 }
323
OnDeactivateLocked()324 void IntelDspStream::OnDeactivateLocked() {
325 LOG(TRACE, "OnDeactivateLocked\n");
326 }
327
OnChannelDeactivateLocked(const dispatcher::Channel & channel)328 void IntelDspStream::OnChannelDeactivateLocked(const dispatcher::Channel& channel) {
329 LOG(TRACE, "OnChannelDeactivateLocked\n");
330 }
331
OnDMAAssignedLocked()332 zx_status_t IntelDspStream::OnDMAAssignedLocked() {
333 LOG(TRACE, "OnDMAAssignedLocked\n");
334 return PublishDeviceLocked();
335 }
336
OnSolicitedResponseLocked(const CodecResponse & resp)337 zx_status_t IntelDspStream::OnSolicitedResponseLocked(const CodecResponse& resp) {
338 return ZX_ERR_NOT_SUPPORTED;
339 }
340
OnUnsolicitedResponseLocked(const CodecResponse & resp)341 zx_status_t IntelDspStream::OnUnsolicitedResponseLocked(const CodecResponse& resp) {
342 return ZX_ERR_NOT_SUPPORTED;
343 }
344
BeginChangeStreamFormatLocked(const audio_proto::StreamSetFmtReq & req)345 zx_status_t IntelDspStream::BeginChangeStreamFormatLocked(
346 const audio_proto::StreamSetFmtReq& req) {
347 LOG(TRACE, "BeginChangeStreamFormatLocked\n");
348 return ZX_OK;
349 }
350
FinishChangeStreamFormatLocked(uint16_t encoded_fmt)351 zx_status_t IntelDspStream::FinishChangeStreamFormatLocked(uint16_t encoded_fmt) {
352 LOG(TRACE, "FinishChangeStreamFormatLocked\n");
353 return ZX_OK;
354 }
355
OnGetGainLocked(audio_proto::GetGainResp * out_resp)356 void IntelDspStream::OnGetGainLocked(audio_proto::GetGainResp* out_resp) {
357 LOG(TRACE, "OnGetGainLocked\n");
358 IntelHDAStreamBase::OnGetGainLocked(out_resp);
359 }
360
OnSetGainLocked(const audio_proto::SetGainReq & req,audio_proto::SetGainResp * out_resp)361 void IntelDspStream::OnSetGainLocked(const audio_proto::SetGainReq& req,
362 audio_proto::SetGainResp* out_resp) {
363 LOG(TRACE, "OnSetGainLocked\n");
364 IntelHDAStreamBase::OnSetGainLocked(req, out_resp);
365 }
366
OnPlugDetectLocked(dispatcher::Channel * response_channel,const audio_proto::PlugDetectReq & req,audio_proto::PlugDetectResp * out_resp)367 void IntelDspStream::OnPlugDetectLocked(dispatcher::Channel* response_channel,
368 const audio_proto::PlugDetectReq& req,
369 audio_proto::PlugDetectResp* out_resp) {
370 LOG(TRACE, "OnPlugDetectLocked\n");
371 IntelHDAStreamBase::OnPlugDetectLocked(response_channel, req, out_resp);
372 }
373
OnGetStringLocked(const audio_proto::GetStringReq & req,audio_proto::GetStringResp * out_resp)374 void IntelDspStream::OnGetStringLocked(const audio_proto::GetStringReq& req,
375 audio_proto::GetStringResp* out_resp) {
376 ZX_DEBUG_ASSERT(out_resp);
377 const char* requested_string = nullptr;
378
379 switch (req.id) {
380 case AUDIO_STREAM_STR_ID_MANUFACTURER:
381 requested_string = "Intel";
382 break;
383
384 case AUDIO_STREAM_STR_ID_PRODUCT:
385 requested_string = is_input() ? "Builtin Microphone" : "Builtin Speakers";
386 break;
387
388 default:
389 IntelHDAStreamBase::OnGetStringLocked(req, out_resp);
390 return;
391 }
392
393 int res = snprintf(reinterpret_cast<char*>(out_resp->str), sizeof(out_resp->str), "%s",
394 requested_string ? requested_string : "<unassigned>");
395 ZX_DEBUG_ASSERT(res >= 0);
396 out_resp->result = ZX_OK;
397 out_resp->strlen = fbl::min<uint32_t>(res, sizeof(out_resp->str) - 1);
398 out_resp->id = req.id;
399 }
400
401 } // namespace intel_hda
402 } // namespace audio
403