| // Copyright 2023 The Pigweed Authors | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not | 
 | // use this file except in compliance with the License. You may obtain a copy of | 
 | // the License at | 
 | // | 
 | //     https://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
 | // License for the specific language governing permissions and limitations under | 
 | // the License. | 
 |  | 
 | #include "pw_mipi_dsi_mcuxpresso/framebuffer_device.h" | 
 |  | 
 | #include "common.h" | 
 | #include "pw_assert/assert.h" | 
 | #include "pw_status/try.h" | 
 |  | 
 | using pw::framebuffer_pool::FramebufferPool; | 
 |  | 
 | namespace pw::mipi::dsi { | 
 |  | 
 | namespace { | 
 | FramebufferDevice::WriteCallback s_write_callback; | 
 | void* s_current_write_buffer; | 
 | }  // namespace | 
 |  | 
 | FramebufferDevice::FramebufferDevice(uint8_t layer) | 
 |     : dc_(nullptr), layer_(layer), enabled_(false) { | 
 |   VIDEO_MEMPOOL_InitEmpty(&video_mempool_); | 
 | } | 
 |  | 
 | Status FramebufferDevice::Init(const dc_fb_t* dc, | 
 |                                const FramebufferPool& framebuffer_pool) { | 
 |   PW_TRY(InitDisplayController(dc)); | 
 |   PW_TRY(InitVideoMemPool(framebuffer_pool)); | 
 |   // Only allow one framebuffer to be checked out at a time. This could be | 
 |   // increased to the pool size if the completion callback mechanism is | 
 |   // improved to allow more than one write at a time. | 
 |   framebuffer_semaphore_.release(1); | 
 |   return OkStatus(); | 
 | } | 
 |  | 
 | Status FramebufferDevice::InitDisplayController(const dc_fb_t* dc) { | 
 |   PW_ASSERT(!dc_); | 
 |   dc_ = dc; | 
 |  | 
 |   status_t status = dc_->ops->init(dc); | 
 |   if (status != kStatus_Success) { | 
 |     return MCUXpressoToPigweedStatus(status); | 
 |   } | 
 |  | 
 |   dc_fb_info_t buff_info; | 
 |  | 
 |   status = dc_->ops->getLayerDefaultConfig(dc_, layer_, &buff_info); | 
 |   if (status != kStatus_Success) { | 
 |     return MCUXpressoToPigweedStatus(status); | 
 |   } | 
 |  | 
 |   dc_->ops->setCallback(dc_, layer_, BufferSwitchOffCallback, this); | 
 |  | 
 |   status = dc_->ops->setLayerConfig(dc_, layer_, &buff_info); | 
 |   if (status != kStatus_Success) { | 
 |     return MCUXpressoToPigweedStatus(status); | 
 |   } | 
 |  | 
 |   return OkStatus(); | 
 | } | 
 |  | 
 | Status FramebufferDevice::InitVideoMemPool( | 
 |     const FramebufferPool& framebuffer_pool) { | 
 |   if (enabled_) { | 
 |     return Status::FailedPrecondition(); | 
 |   } | 
 |  | 
 |   const FramebufferPool::BufferArray& buffers = | 
 |       framebuffer_pool.GetBuffersForInit(); | 
 |   for (size_t i = 0; i < buffers.size(); i++) { | 
 |     VIDEO_MEMPOOL_Put(&video_mempool_, buffers[i]); | 
 |   } | 
 |  | 
 |   return OkStatus(); | 
 | } | 
 |  | 
 | Status FramebufferDevice::Close() { | 
 |   if (!dc_) | 
 |     return Status::FailedPrecondition(); | 
 |  | 
 |   status_t status = dc_->ops->deinit(dc_); | 
 |   dc_ = nullptr; | 
 |   return MCUXpressoToPigweedStatus(status); | 
 | } | 
 |  | 
 | Status FramebufferDevice::Enable() { | 
 |   if (enabled_) | 
 |     return OkStatus(); | 
 |  | 
 |   status_t status = kStatus_Success; | 
 |  | 
 |   if ((dc_->ops->getProperty(dc_) & (uint32_t)kDC_FB_ReserveFrameBuffer) == | 
 |       0U) { | 
 |     status = dc_->ops->enableLayer(dc_, layer_); | 
 |     if (status != kStatus_Success) { | 
 |       enabled_ = true; | 
 |     } | 
 |   } | 
 |  | 
 |   return MCUXpressoToPigweedStatus(status); | 
 | } | 
 |  | 
 | Status FramebufferDevice::Disable() { | 
 |   if (!enabled_) | 
 |     return OkStatus(); | 
 |  | 
 |   status_t status = dc_->ops->disableLayer(dc_, layer_); | 
 |   enabled_ = false; | 
 |  | 
 |   return MCUXpressoToPigweedStatus(status); | 
 | } | 
 |  | 
 | void FramebufferDevice::WriteFramebuffer(void* frameBuffer, | 
 |                                          WriteCallback write_callback) { | 
 |   PW_ASSERT(!s_write_callback); | 
 |   s_write_callback = std::move(write_callback); | 
 |   s_current_write_buffer = frameBuffer; | 
 |   Status s = MCUXpressoToPigweedStatus( | 
 |       dc_->ops->setFrameBuffer(dc_, layer_, frameBuffer)); | 
 |   if (!s.ok()) | 
 |     WriteComplete(s_current_write_buffer, s); | 
 | } | 
 |  | 
 | void* FramebufferDevice::GetFramebuffer() { | 
 |   framebuffer_semaphore_.acquire(); | 
 |   return VIDEO_MEMPOOL_Get(&video_mempool_); | 
 | } | 
 |  | 
 | void FramebufferDevice::WriteComplete(void* buffer, Status s) { | 
 |   PW_ASSERT(buffer == s_current_write_buffer); | 
 |   PW_ASSERT(s_write_callback); | 
 |   framebuffer_semaphore_.release(); | 
 |   s_current_write_buffer = nullptr; | 
 |   s_write_callback(buffer, s); | 
 | } | 
 |  | 
 | void FramebufferDevice::BufferSwitchOff(void* buffer) { | 
 |   VIDEO_MEMPOOL_Put(&video_mempool_, buffer); | 
 |   WriteComplete(buffer, OkStatus()); | 
 | } | 
 |  | 
 | // static | 
 | void FramebufferDevice::BufferSwitchOffCallback(void* param, void* buffer) { | 
 |   PW_ASSERT(param != nullptr); | 
 |   static_cast<FramebufferDevice*>(param)->BufferSwitchOff(buffer); | 
 | } | 
 |  | 
 | }  // namespace pw::mipi::dsi |