| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #pragma once |
| |
| #include <ddk/binding.h> |
| #include <ddk/device.h> |
| #include <ddk/protocol/intelhda/codec.h> |
| #include <lib/zx/handle.h> |
| #include <fbl/mutex.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/unique_ptr.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <zircon/thread_annotations.h> |
| |
| #include <dispatcher-pool/dispatcher-channel.h> |
| #include <dispatcher-pool/dispatcher-execution-domain.h> |
| #include <intel-hda/utils/codec-commands.h> |
| #include <intel-hda/utils/intel-hda-proto.h> |
| #include <intel-hda/utils/intel-hda-registers.h> |
| |
| #include "codec-cmd-job.h" |
| #include "debug-logging.h" |
| #include "intel-hda-stream.h" |
| |
| namespace audio { |
| namespace intel_hda { |
| |
| class IntelHDAController; |
| struct CodecResponse; |
| |
| class IntelHDACodec : public fbl::RefCounted<IntelHDACodec> { |
| public: |
| enum class State { |
| PROBING, |
| FINDING_DRIVER, |
| OPERATING, |
| SHUTTING_DOWN, |
| SHUT_DOWN, |
| FATAL_ERROR, |
| }; |
| |
| static fbl::RefPtr<IntelHDACodec> Create(IntelHDAController& controller, uint8_t codec_id); |
| |
| zx_status_t Startup(); |
| void ProcessSolicitedResponse(const CodecResponse& resp, fbl::unique_ptr<CodecCmdJob>&& job); |
| void ProcessUnsolicitedResponse(const CodecResponse& resp); |
| void ProcessWakeupEvt(); |
| |
| // TODO (johngro) : figure out shutdown... Currently, this expected to |
| // execute synchronously, which does not allow codec drivers any opportunity |
| // to perform a graceful shutdown. |
| // |
| // OTOH - If our driver is being unloaded by the device manager, in theory, |
| // it should have already unloaded all of the codecs, giving them a chances |
| // to quiesce their hardware in the process. |
| void Shutdown(); |
| |
| uint8_t id() const { return codec_id_; } |
| State state() const { return state_; } |
| const char* log_prefix() const { return log_prefix_; } |
| |
| // Debug/Diags |
| void DumpState(); |
| |
| private: |
| friend class fbl::RefPtr<IntelHDACodec>; |
| |
| using ProbeParseCbk = zx_status_t (IntelHDACodec::*)(const CodecResponse& resp); |
| struct ProbeCommandListEntry { |
| CodecVerb verb; |
| ProbeParseCbk parse; |
| }; |
| |
| static constexpr size_t PROP_PROTOCOL = 0; |
| static constexpr size_t PROP_VID = 1; |
| static constexpr size_t PROP_DID = 2; |
| static constexpr size_t PROP_MAJOR_REV = 3; |
| static constexpr size_t PROP_MINOR_REV = 4; |
| static constexpr size_t PROP_VENDOR_REV = 5; |
| static constexpr size_t PROP_VENDOR_STEP = 6; |
| static constexpr size_t PROP_COUNT = 7; |
| |
| static zx_protocol_device_t CODEC_DEVICE_THUNKS; |
| static ihda_codec_protocol_ops_t CODEC_PROTO_THUNKS; |
| |
| IntelHDACodec(IntelHDAController& controller, uint8_t codec_id); |
| virtual ~IntelHDACodec() { ZX_DEBUG_ASSERT(state_ == State::SHUT_DOWN); } |
| |
| zx_status_t PublishDevice(); |
| |
| void SendCORBResponse(const fbl::RefPtr<dispatcher::Channel>& channel, |
| const CodecResponse& resp, |
| uint32_t transaction_id = IHDA_INVALID_TRANSACTION_ID); |
| |
| // Parsers for device probing |
| zx_status_t ParseVidDid(const CodecResponse& resp); |
| zx_status_t ParseRevisionId(const CodecResponse& resp); |
| |
| // ZX_PROTOCOL_IHDA_CODEC Interface |
| zx_status_t CodecGetDispatcherChannel(zx_handle_t* channel_out); |
| |
| // Thunks for interacting with clients and codec drivers. |
| zx_status_t DeviceIoctl(uint32_t op, void* out_buf, size_t out_len, size_t* out_actual); |
| zx_status_t ProcessClientRequest(dispatcher::Channel* channel, bool is_driver_channel); |
| void ProcessClientDeactivate(const dispatcher::Channel* channel); |
| zx_status_t ProcessGetIDs(dispatcher::Channel* channel, const ihda_proto::GetIDsReq& req); |
| zx_status_t ProcessSendCORBCmd(dispatcher::Channel* channel, |
| const ihda_proto::SendCORBCmdReq& req); |
| zx_status_t ProcessRequestStream(dispatcher::Channel* channel, |
| const ihda_proto::RequestStreamReq& req); |
| zx_status_t ProcessReleaseStream(dispatcher::Channel* channel, |
| const ihda_proto::ReleaseStreamReq& req); |
| zx_status_t ProcessSetStreamFmt(dispatcher::Channel* channel, |
| const ihda_proto::SetStreamFmtReq& req); |
| |
| // Reference to our owner. |
| IntelHDAController& controller_; |
| |
| // State management. |
| State state_ = State::PROBING; |
| uint probe_rx_ndx_ = 0; |
| |
| // Driver connection state |
| fbl::Mutex codec_driver_channel_lock_; |
| fbl::RefPtr<dispatcher::Channel> codec_driver_channel_ TA_GUARDED(codec_driver_channel_lock_); |
| |
| // Device properties. |
| const uint8_t codec_id_; |
| zx_device_prop_t dev_props_[PROP_COUNT]; |
| zx_device_t* dev_node_ = nullptr; |
| struct { |
| uint16_t vid; |
| uint16_t did; |
| uint8_t ihda_vmaj; |
| uint8_t ihda_vmin; |
| uint8_t rev_id; |
| uint8_t step_id; |
| } props_; |
| |
| // Log prefix storage |
| char log_prefix_[LOG_PREFIX_STORAGE] = { 0 }; |
| |
| // Dispatcher framework state. |
| fbl::RefPtr<dispatcher::ExecutionDomain> default_domain_; |
| |
| // Active DMA streams |
| fbl::Mutex active_streams_lock_; |
| IntelHDAStream::Tree active_streams_ TA_GUARDED(active_streams_lock_); |
| |
| static ProbeCommandListEntry PROBE_COMMANDS[]; |
| }; |
| |
| } // namespace intel_hda |
| } // namespace audio |