• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
18 
19 #include "HistogramDevice.h"
20 
21 #include <drm/samsung_drm.h>
22 
23 #include <sstream>
24 #include <string>
25 
26 #include "ExynosDisplayDrmInterface.h"
27 #include "ExynosHWCHelper.h"
28 #include "android-base/macros.h"
29 
30 /**
31  * histogramOnBinderDied
32  *
33  * The binderdied callback function which is registered in registerHistogram and would trigger
34  * unregisterHistogram to cleanup the channel.
35  *
36  * @cookie pointer to the TokenInfo of the binder object.
37  */
histogramOnBinderDied(void * cookie)38 static void histogramOnBinderDied(void *cookie) {
39     ATRACE_CALL();
40     HistogramDevice::HistogramErrorCode histogramErrorCode;
41     HistogramDevice::TokenInfo *tokenInfo = (HistogramDevice::TokenInfo *)cookie;
42     ALOGI("%s: histogram channel #%u: client with token(%p) is died", __func__,
43           tokenInfo->channelId, tokenInfo->token.get());
44 
45     /* release the histogram channel */
46     tokenInfo->histogramDevice->unregisterHistogram(tokenInfo->token, &histogramErrorCode);
47 
48     if (histogramErrorCode != HistogramDevice::HistogramErrorCode::NONE) {
49         ALOGE("%s: histogram channel #%u: failed to unregisterHistogram: %s", __func__,
50               tokenInfo->channelId,
51               aidl::com::google::hardware::pixel::display::toString(histogramErrorCode).c_str());
52     }
53 }
54 
ChannelInfo()55 HistogramDevice::ChannelInfo::ChannelInfo()
56       : status(ChannelStatus_t::DISABLED),
57         token(nullptr),
58         pid(-1),
59         requestedRoi(),
60         workingConfig(),
61         threshold(0),
62         histDataCollecting(false) {}
63 
ChannelInfo(const ChannelInfo & other)64 HistogramDevice::ChannelInfo::ChannelInfo(const ChannelInfo &other) {
65     std::scoped_lock lock(other.channelInfoMutex);
66     status = other.status;
67     token = other.token;
68     pid = other.pid;
69     requestedRoi = other.requestedRoi;
70     workingConfig = other.workingConfig;
71     threshold = other.threshold;
72     histDataCollecting = other.histDataCollecting;
73 }
74 
HistogramDevice(ExynosDisplay * display,uint8_t channelCount,std::vector<uint8_t> reservedChannels)75 HistogramDevice::HistogramDevice(ExynosDisplay *display, uint8_t channelCount,
76                                  std::vector<uint8_t> reservedChannels) {
77     mDisplay = display;
78 
79     // TODO: b/295786065 - Get available channels from crtc property.
80     initChannels(channelCount, reservedChannels);
81 
82     /* Create the death recipient which will be deleted in the destructor */
83     mDeathRecipient = AIBinder_DeathRecipient_new(histogramOnBinderDied);
84 }
85 
~HistogramDevice()86 HistogramDevice::~HistogramDevice() {
87     if (mDeathRecipient) {
88         AIBinder_DeathRecipient_delete(mDeathRecipient);
89     }
90 }
91 
initDrm(const DrmCrtc & crtc)92 void HistogramDevice::initDrm(const DrmCrtc &crtc) {
93     // TODO: b/295786065 - Get available channels from crtc property.
94 
95     // TODO: b/295786065 - Check if the multi channel property is supported.
96     initHistogramCapability(crtc.histogram_channel_property(0).id() != 0);
97 }
98 
getHistogramCapability(HistogramCapability * histogramCapability) const99 ndk::ScopedAStatus HistogramDevice::getHistogramCapability(
100         HistogramCapability *histogramCapability) const {
101     ATRACE_CALL();
102 
103     if (!histogramCapability) {
104         ALOGE("%s: binder error, histogramCapability is nullptr", __func__);
105         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
106     }
107 
108     *histogramCapability = mHistogramCapability;
109 
110     return ndk::ScopedAStatus::ok();
111 }
112 
registerHistogram(const ndk::SpAIBinder & token,const HistogramConfig & histogramConfig,HistogramErrorCode * histogramErrorCode)113 ndk::ScopedAStatus HistogramDevice::registerHistogram(const ndk::SpAIBinder &token,
114                                                       const HistogramConfig &histogramConfig,
115                                                       HistogramErrorCode *histogramErrorCode) {
116     ATRACE_CALL();
117 
118     if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) {
119         ALOGE("%s: histogram interface is not supported", __func__);
120         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
121     }
122 
123     return configHistogram(token, histogramConfig, histogramErrorCode, false);
124 }
125 
queryHistogram(const ndk::SpAIBinder & token,std::vector<char16_t> * histogramBuffer,HistogramErrorCode * histogramErrorCode)126 ndk::ScopedAStatus HistogramDevice::queryHistogram(const ndk::SpAIBinder &token,
127                                                    std::vector<char16_t> *histogramBuffer,
128                                                    HistogramErrorCode *histogramErrorCode) {
129     ATRACE_CALL();
130 
131     if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) {
132         ALOGE("%s: histogram interface is not supported", __func__);
133         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
134     }
135 
136     /* No need to validate the argument (token), if the token is not correct it cannot be converted
137      * to the channel id later. */
138 
139     /* validate the argument (histogramBuffer) */
140     if (!histogramBuffer) {
141         ALOGE("%s: binder error, histogramBuffer is nullptr", __func__);
142         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
143     }
144 
145     /* validate the argument (histogramErrorCode) */
146     if (!histogramErrorCode) {
147         ALOGE("%s: binder error, histogramErrorCode is nullptr", __func__);
148         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
149     }
150 
151     /* default histogramErrorCode: no error */
152     *histogramErrorCode = HistogramErrorCode::NONE;
153 
154     if (mDisplay->isPowerModeOff()) {
155         ALOGW("%s: DISPLAY_POWEROFF, histogram is not available when display is off", __func__);
156         *histogramErrorCode = HistogramErrorCode::DISPLAY_POWEROFF;
157         return ndk::ScopedAStatus::ok();
158     }
159 
160     if (mDisplay->isSecureContentPresenting()) {
161         ALOGV("%s: DRM_PLAYING, histogram is not available when secure content is presenting",
162               __func__);
163         *histogramErrorCode = HistogramErrorCode::DRM_PLAYING;
164         return ndk::ScopedAStatus::ok();
165     }
166 
167     uint8_t channelId;
168 
169     /* Hold the mAllocatorMutex for a short time just to convert the token to channel id. Prevent
170      * holding the mAllocatorMutex when waiting for the histogram data back which may takes several
171      * milliseconds */
172     {
173         ATRACE_NAME("getChannelId");
174         std::scoped_lock lock(mAllocatorMutex);
175         if ((*histogramErrorCode = getChannelIdByTokenLocked(token, channelId)) !=
176             HistogramErrorCode::NONE) {
177             return ndk::ScopedAStatus::ok();
178         }
179     }
180 
181     getHistogramData(channelId, histogramBuffer, histogramErrorCode);
182 
183     /* Clear the histogramBuffer when error occurs */
184     if (*histogramErrorCode != HistogramErrorCode::NONE) {
185         histogramBuffer->assign(HISTOGRAM_BIN_COUNT, 0);
186     }
187 
188     return ndk::ScopedAStatus::ok();
189 }
190 
reconfigHistogram(const ndk::SpAIBinder & token,const HistogramConfig & histogramConfig,HistogramErrorCode * histogramErrorCode)191 ndk::ScopedAStatus HistogramDevice::reconfigHistogram(const ndk::SpAIBinder &token,
192                                                       const HistogramConfig &histogramConfig,
193                                                       HistogramErrorCode *histogramErrorCode) {
194     ATRACE_CALL();
195 
196     if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) {
197         ALOGE("%s: histogram interface is not supported", __func__);
198         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
199     }
200 
201     return configHistogram(token, histogramConfig, histogramErrorCode, true);
202 }
203 
unregisterHistogram(const ndk::SpAIBinder & token,HistogramErrorCode * histogramErrorCode)204 ndk::ScopedAStatus HistogramDevice::unregisterHistogram(const ndk::SpAIBinder &token,
205                                                         HistogramErrorCode *histogramErrorCode) {
206     ATRACE_CALL();
207 
208     if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) {
209         ALOGE("%s: histogram interface is not supported", __func__);
210         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
211     }
212 
213     /* No need to validate the argument (token), if the token is not correct it cannot be converted
214      * to the channel id later. */
215 
216     /* validate the argument (histogramErrorCode) */
217     if (!histogramErrorCode) {
218         ALOGE("%s: binder error, histogramErrorCode is nullptr", __func__);
219         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
220     }
221 
222     /* default histogramErrorCode: no error */
223     *histogramErrorCode = HistogramErrorCode::NONE;
224 
225     ATRACE_BEGIN("getChannelId");
226     uint8_t channelId;
227     std::scoped_lock lock(mAllocatorMutex);
228 
229     if ((*histogramErrorCode = getChannelIdByTokenLocked(token, channelId)) !=
230         HistogramErrorCode::NONE) {
231         ATRACE_END();
232         return ndk::ScopedAStatus::ok();
233     }
234     ATRACE_END();
235 
236     releaseChannelLocked(channelId);
237 
238     /*
239      * If AIBinder is alive, the unregisterHistogram is triggered from the histogram client, and we
240      * need to unlink the binder object from death notification.
241      * If AIBinder is already dead, the unregisterHistogram is triggered from binderdied callback,
242      * no need to unlink here.
243      */
244     if (LIKELY(AIBinder_isAlive(token.get()))) {
245         binder_status_t status;
246         if ((status = AIBinder_unlinkToDeath(token.get(), mDeathRecipient,
247                                              &mTokenInfoMap[token.get()]))) {
248             /* Not return error due to the AIBinder_unlinkToDeath */
249             ALOGE("%s: histogram channel #%u: AIBinder_linkToDeath error %d", __func__, channelId,
250                   status);
251         }
252     }
253 
254     /* Delete the corresponding TokenInfo after the binder object is already unlinked. */
255     mTokenInfoMap.erase(token.get());
256 
257     return ndk::ScopedAStatus::ok();
258 }
259 
handleDrmEvent(void * event)260 void HistogramDevice::handleDrmEvent(void *event) {
261     int ret = NO_ERROR;
262     uint8_t channelId;
263     char16_t *buffer;
264 
265     if ((ret = parseDrmEvent(event, channelId, buffer))) {
266         ALOGE("%s: failed to parseDrmEvent, ret %d", __func__, ret);
267         return;
268     }
269 
270     ATRACE_NAME(String8::format("handleHistogramDrmEvent #%u", channelId).string());
271     if (channelId >= mChannels.size()) {
272         ALOGE("%s: histogram channel #%u: invalid channelId", __func__, channelId);
273         return;
274     }
275 
276     ChannelInfo &channel = mChannels[channelId];
277     std::unique_lock<std::mutex> lock(channel.histDataCollectingMutex);
278 
279     /* Check if the histogram channel is collecting the histogram data */
280     if (channel.histDataCollecting == true) {
281         std::memcpy(channel.histData, buffer, HISTOGRAM_BIN_COUNT * sizeof(char16_t));
282         channel.histDataCollecting = false;
283     } else {
284         ALOGW("%s: histogram channel #%u: ignore the histogram channel event", __func__, channelId);
285     }
286 
287     channel.histDataCollecting_cv.notify_all();
288 }
289 
prepareAtomicCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq & drmReq)290 void HistogramDevice::prepareAtomicCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq) {
291     ATRACE_NAME("HistogramAtomicCommit");
292 
293     /* Loop through every channel and call prepareChannelCommit */
294     for (uint8_t channelId = 0; channelId < mChannels.size(); ++channelId) {
295         int channelRet = prepareChannelCommit(drmReq, channelId);
296 
297         /* Every channel is independent, no early return when the channel commit fails. */
298         if (channelRet) {
299             ALOGE("%s: histogram channel #%u: failed to prepare atomic commit: %d", __func__,
300                   channelId, channelRet);
301         }
302     }
303 }
304 
postAtomicCommit()305 void HistogramDevice::postAtomicCommit() {
306     /* Atomic commit is success, loop through every channel and update the channel status */
307     for (uint8_t channelId = 0; channelId < mChannels.size(); ++channelId) {
308         ChannelInfo &channel = mChannels[channelId];
309         std::scoped_lock lock(channel.channelInfoMutex);
310 
311         switch (channel.status) {
312             case ChannelStatus_t::CONFIG_BLOB_ADDED:
313                 channel.status = ChannelStatus_t::CONFIG_COMMITTED;
314                 break;
315             case ChannelStatus_t::DISABLE_BLOB_ADDED:
316                 channel.status = ChannelStatus_t::DISABLED;
317                 break;
318             default:
319                 break;
320         }
321     }
322 }
323 
dump(String8 & result) const324 void HistogramDevice::dump(String8 &result) const {
325     /* Do not dump the Histogram Device if it is not supported. */
326     if (!mHistogramCapability.supportMultiChannel) {
327         return;
328     }
329 
330     /* print the histogram capability */
331     dumpHistogramCapability(result);
332 
333     result.appendFormat("\n");
334 
335     /* print the histogram channel info*/
336     result.appendFormat("Histogram channel info:\n");
337     for (uint8_t channelId = 0; channelId < mChannels.size(); ++channelId) {
338         // TODO: b/294489887 - Use buildForMiniDump can eliminate the redundant rows.
339         TableBuilder tb;
340         const ChannelInfo &channel = mChannels[channelId];
341         std::scoped_lock lock(channel.channelInfoMutex);
342         tb.add("ID", (int)channelId);
343         tb.add("status", toString(channel.status));
344         tb.add("token", channel.token.get());
345         tb.add("pid", channel.pid);
346         tb.add("requestedRoi", toString(channel.requestedRoi));
347         tb.add("workingRoi", toString(channel.workingConfig.roi));
348         tb.add("threshold", channel.threshold);
349         tb.add("weightRGB", toString(channel.workingConfig.weights));
350         tb.add("samplePos",
351                aidl::com::google::hardware::pixel::display::toString(
352                        channel.workingConfig.samplePos));
353         result.append(tb.build().c_str());
354     }
355 
356     result.appendFormat("\n");
357 }
358 
initChannels(uint8_t channelCount,const std::vector<uint8_t> & reservedChannels)359 void HistogramDevice::initChannels(uint8_t channelCount,
360                                    const std::vector<uint8_t> &reservedChannels) {
361     mChannels.resize(channelCount);
362     ALOGI("%s: init histogram with %d channels", __func__, channelCount);
363 
364     for (const uint8_t reservedChannelId : reservedChannels) {
365         if (reservedChannelId < mChannels.size()) {
366             std::scoped_lock channelLock(mChannels[reservedChannelId].channelInfoMutex);
367             mChannels[reservedChannelId].status = ChannelStatus_t::RESERVED;
368         }
369     }
370 
371     std::scoped_lock lock(mAllocatorMutex);
372     for (uint8_t channelId = 0; channelId < channelCount; ++channelId) {
373         std::scoped_lock channelLock(mChannels[channelId].channelInfoMutex);
374 
375         if (mChannels[channelId].status == ChannelStatus_t::RESERVED) {
376             ALOGI("%s: histogram channel #%u: reserved for driver", __func__, (int)channelId);
377             continue;
378         }
379 
380         mFreeChannels.push(channelId);
381     }
382 }
383 
initHistogramCapability(bool supportMultiChannel)384 void HistogramDevice::initHistogramCapability(bool supportMultiChannel) {
385     ExynosDisplayDrmInterface *moduleDisplayInterface =
386             static_cast<ExynosDisplayDrmInterface *>(mDisplay->mDisplayInterface.get());
387 
388     if (!moduleDisplayInterface) {
389         ALOGE("%s: failed to get ExynosDisplayDrmInterface (nullptr), cannot get panel full "
390               "resolution",
391               __func__);
392         mHistogramCapability.fullResolutionWidth = 0;
393         mHistogramCapability.fullResolutionHeight = 0;
394     } else {
395         mHistogramCapability.fullResolutionWidth =
396                 moduleDisplayInterface->getPanelFullResolutionHSize();
397         mHistogramCapability.fullResolutionHeight =
398                 moduleDisplayInterface->getPanelFullResolutionVSize();
399     }
400     ALOGI("%s: full screen roi: (0,0)x(%dx%d)", __func__, mHistogramCapability.fullResolutionWidth,
401           mHistogramCapability.fullResolutionHeight);
402 
403     mHistogramCapability.channelCount = mChannels.size();
404     mHistogramCapability.supportMultiChannel = supportMultiChannel;
405 
406     initSupportSamplePosList();
407 }
408 
initSupportSamplePosList()409 void HistogramDevice::initSupportSamplePosList() {
410     mHistogramCapability.supportSamplePosList.push_back(HistogramSamplePos::PRE_POSTPROC);
411     mHistogramCapability.supportSamplePosList.push_back(HistogramSamplePos::POST_POSTPROC);
412 }
413 
configHistogram(const ndk::SpAIBinder & token,const HistogramConfig & histogramConfig,HistogramErrorCode * histogramErrorCode,bool isReconfig)414 ndk::ScopedAStatus HistogramDevice::configHistogram(const ndk::SpAIBinder &token,
415                                                     const HistogramConfig &histogramConfig,
416                                                     HistogramErrorCode *histogramErrorCode,
417                                                     bool isReconfig) {
418     /* validate the argument (histogramErrorCode) */
419     if (!histogramErrorCode) {
420         ALOGE("%s: binder error, histogramErrorCode is nullptr", __func__);
421         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
422     }
423 
424     /* default histogramErrorCode: no error */
425     *histogramErrorCode = HistogramErrorCode::NONE;
426 
427     /* validate the argument (token) */
428     if (token.get() == nullptr) {
429         ALOGE("%s: BAD_TOKEN, token is nullptr", __func__);
430         *histogramErrorCode = HistogramErrorCode::BAD_TOKEN;
431         return ndk::ScopedAStatus::ok();
432     }
433 
434     /* validate the argument (histogramConfig) */
435     if ((*histogramErrorCode = validateHistogramConfig(histogramConfig)) !=
436         HistogramErrorCode::NONE) {
437         return ndk::ScopedAStatus::ok();
438     }
439 
440     /* threshold is calculated based on the roi coordinates rather than configured by the client */
441     int threshold = calculateThreshold(histogramConfig.roi);
442 
443     {
444         ATRACE_BEGIN("getOrAcquireChannelId");
445         uint8_t channelId;
446         std::scoped_lock lock(mAllocatorMutex);
447 
448         /* isReconfig is false: registerHistogram, need to allcoate the histogram channel
449          * isReconfig is true: reconfigHistogram, already registered, only need to get channel id
450          */
451         if (!isReconfig) {
452             if ((*histogramErrorCode = acquireChannelLocked(token, channelId)) !=
453                 HistogramErrorCode::NONE) {
454                 ATRACE_END();
455                 return ndk::ScopedAStatus::ok();
456             }
457         } else {
458             if ((*histogramErrorCode = getChannelIdByTokenLocked(token, channelId)) !=
459                 HistogramErrorCode::NONE) {
460                 ATRACE_END();
461                 return ndk::ScopedAStatus::ok();
462             }
463         }
464         ATRACE_END();
465 
466         /* store the histogram information, and mark the channel ready for atomic commit by setting
467          * the status to CONFIG_PENDING */
468         fillupChannelInfo(channelId, token, histogramConfig, threshold);
469 
470         if (!isReconfig) {
471             /* link the binder object (token) to the death recipient. When the binder object is
472              * destructed, the callback function histogramOnBinderDied can release the histogram
473              * resources automatically. */
474             binder_status_t status;
475             if ((status = AIBinder_linkToDeath(token.get(), mDeathRecipient,
476                                                &mTokenInfoMap[token.get()]))) {
477                 /* Not return error due to the AIBinder_linkToDeath because histogram function can
478                  * still work */
479                 ALOGE("%s: histogram channel #%u: AIBinder_linkToDeath error %d", __func__,
480                       channelId, status);
481             }
482         }
483     }
484 
485     if (!mDisplay->isPowerModeOff()) {
486         mDisplay->mDevice->onRefresh(mDisplay->mDisplayId);
487     }
488 
489     return ndk::ScopedAStatus::ok();
490 }
491 
getHistogramData(uint8_t channelId,std::vector<char16_t> * histogramBuffer,HistogramErrorCode * histogramErrorCode)492 void HistogramDevice::getHistogramData(uint8_t channelId, std::vector<char16_t> *histogramBuffer,
493                                        HistogramErrorCode *histogramErrorCode) {
494     ATRACE_NAME(String8::format("%s #%u", __func__, channelId).string());
495     int32_t ret;
496     ExynosDisplayDrmInterface *moduleDisplayInterface =
497             static_cast<ExynosDisplayDrmInterface *>(mDisplay->mDisplayInterface.get());
498     if (!moduleDisplayInterface) {
499         *histogramErrorCode = HistogramErrorCode::BAD_HIST_DATA;
500         ALOGE("%s: histogram channel #%u: BAD_HIST_DATA, moduleDisplayInterface is nullptr",
501               __func__, channelId);
502         return;
503     }
504 
505     ChannelInfo &channel = mChannels[channelId];
506 
507     std::unique_lock<std::mutex> lock(channel.histDataCollectingMutex);
508 
509     /* Check if the previous queryHistogram is finished */
510     if (channel.histDataCollecting) {
511         *histogramErrorCode = HistogramErrorCode::BAD_HIST_DATA;
512         ALOGE("%s: histogram channel #%u: BAD_HIST_DATA, previous %s not finished", __func__,
513               channelId, __func__);
514         return;
515     }
516 
517     /* Send the ioctl request (histogram_channel_request_ioctl) which allocate the drm event and
518      * send back the drm event with data when available. */
519     if ((ret = moduleDisplayInterface->sendHistogramChannelIoctl(HistogramChannelIoctl_t::REQUEST,
520                                                                  channelId)) != NO_ERROR) {
521         *histogramErrorCode = HistogramErrorCode::BAD_HIST_DATA;
522         ALOGE("%s: histogram channel #%u: BAD_HIST_DATA, sendHistogramChannelIoctl (REQUEST) "
523               "error "
524               "(%d)",
525               __func__, channelId, ret);
526         return;
527     }
528     channel.histDataCollecting = true;
529 
530     {
531         ATRACE_NAME(String8::format("waitDrmEvent #%u", channelId).string());
532         /* Wait until the condition variable is notified or timeout. */
533         channel.histDataCollecting_cv.wait_for(lock, std::chrono::milliseconds(50),
534                                                [this, &channel]() {
535                                                    return (!mDisplay->isPowerModeOff() &&
536                                                            !channel.histDataCollecting);
537                                                });
538     }
539 
540     /* If the histDataCollecting is not cleared, check the reason and clear the histogramBuffer.
541      */
542     if (channel.histDataCollecting) {
543         if (mDisplay->isPowerModeOff()) {
544             *histogramErrorCode = HistogramErrorCode::DISPLAY_POWEROFF;
545             ALOGW("%s: histogram channel #%u: DISPLAY_POWEROFF, histogram is not available "
546                   "when "
547                   "display is off",
548                   __func__, channelId);
549         } else {
550             *histogramErrorCode = HistogramErrorCode::BAD_HIST_DATA;
551             ALOGE("%s: histogram channel #%u: BAD_HIST_DATA, no histogram channel event is "
552                   "handled",
553                   __func__, channelId);
554         }
555 
556         /* Cancel the histogram data request */
557         ALOGI("%s: histogram channel #%u: cancel histogram data request", __func__, channelId);
558         if ((ret = moduleDisplayInterface
559                            ->sendHistogramChannelIoctl(HistogramChannelIoctl_t::CANCEL,
560                                                        channelId)) != NO_ERROR) {
561             ALOGE("%s: histogram channel #%u: sendHistogramChannelIoctl (CANCEL) error (%d)",
562                   __func__, channelId, ret);
563         }
564 
565         channel.histDataCollecting = false;
566         return;
567     }
568 
569     if (mDisplay->isSecureContentPresenting()) {
570         ALOGV("%s: histogram channel #%u: DRM_PLAYING, histogram is not available when secure "
571               "content is presenting",
572               __func__, channelId);
573         *histogramErrorCode = HistogramErrorCode::DRM_PLAYING;
574         return;
575     }
576 
577     /* Copy the histogram data from histogram info to histogramBuffer */
578     histogramBuffer->assign(channel.histData, channel.histData + HISTOGRAM_BIN_COUNT);
579 }
580 
parseDrmEvent(void * event,uint8_t & channelId,char16_t * & buffer) const581 int HistogramDevice::parseDrmEvent(void *event, uint8_t &channelId, char16_t *&buffer) const {
582     channelId = 0;
583     buffer = nullptr;
584     return INVALID_OPERATION;
585 }
586 
acquireChannelLocked(const ndk::SpAIBinder & token,uint8_t & channelId)587 HistogramDevice::HistogramErrorCode HistogramDevice::acquireChannelLocked(
588         const ndk::SpAIBinder &token, uint8_t &channelId) {
589     ATRACE_CALL();
590     if (mFreeChannels.size() == 0) {
591         ALOGE("%s: NO_CHANNEL_AVAILABLE, there is no histogram channel available", __func__);
592         return HistogramErrorCode::NO_CHANNEL_AVAILABLE;
593     }
594 
595     if (mTokenInfoMap.find(token.get()) != mTokenInfoMap.end()) {
596         ALOGE("%s: BAD_TOKEN, token (%p) is already registered", __func__, token.get());
597         return HistogramErrorCode::BAD_TOKEN;
598     }
599 
600     /* Acquire free channel id from the free list */
601     channelId = mFreeChannels.front();
602     mFreeChannels.pop();
603     mTokenInfoMap[token.get()] = {.channelId = channelId, .histogramDevice = this, .token = token};
604 
605     return HistogramErrorCode::NONE;
606 }
607 
releaseChannelLocked(uint8_t channelId)608 void HistogramDevice::releaseChannelLocked(uint8_t channelId) {
609     /* Add the channel id back to the free list and cleanup the channel info with status set to
610      * DISABLE_PENDING */
611     mFreeChannels.push(channelId);
612     cleanupChannelInfo(channelId);
613 }
614 
getChannelIdByTokenLocked(const ndk::SpAIBinder & token,uint8_t & channelId)615 HistogramDevice::HistogramErrorCode HistogramDevice::getChannelIdByTokenLocked(
616         const ndk::SpAIBinder &token, uint8_t &channelId) {
617     if (mTokenInfoMap.find(token.get()) == mTokenInfoMap.end()) {
618         ALOGE("%s: BAD_TOKEN, token (%p) is not registered", __func__, token.get());
619         return HistogramErrorCode::BAD_TOKEN;
620     }
621 
622     channelId = mTokenInfoMap[token.get()].channelId;
623 
624     return HistogramErrorCode::NONE;
625 }
626 
cleanupChannelInfo(uint8_t channelId)627 void HistogramDevice::cleanupChannelInfo(uint8_t channelId) {
628     ATRACE_NAME(String8::format("%s #%u", __func__, channelId).string());
629     ChannelInfo &channel = mChannels[channelId];
630     std::scoped_lock lock(channel.channelInfoMutex);
631     channel.status = ChannelStatus_t::DISABLE_PENDING;
632     channel.token = nullptr;
633     channel.pid = -1;
634     channel.requestedRoi = {.left = 0, .top = 0, .right = 0, .bottom = 0};
635     channel.workingConfig = {.roi.left = 0,
636                              .roi.top = 0,
637                              .roi.right = 0,
638                              .roi.bottom = 0,
639                              .weights.weightR = 0,
640                              .weights.weightG = 0,
641                              .weights.weightB = 0,
642                              .samplePos = HistogramSamplePos::POST_POSTPROC};
643     channel.threshold = 0;
644 }
645 
fillupChannelInfo(uint8_t channelId,const ndk::SpAIBinder & token,const HistogramConfig & histogramConfig,int threshold)646 void HistogramDevice::fillupChannelInfo(uint8_t channelId, const ndk::SpAIBinder &token,
647                                         const HistogramConfig &histogramConfig, int threshold) {
648     ATRACE_NAME(String8::format("%s #%u", __func__, channelId).string());
649     ChannelInfo &channel = mChannels[channelId];
650     std::scoped_lock lock(channel.channelInfoMutex);
651     channel.status = ChannelStatus_t::CONFIG_PENDING;
652     channel.token = token;
653     channel.pid = AIBinder_getCallingPid();
654     channel.requestedRoi = histogramConfig.roi;
655     channel.workingConfig = histogramConfig;
656     channel.workingConfig.roi = {};
657     channel.threshold = threshold;
658 }
659 
prepareChannelCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq & drmReq,uint8_t channelId)660 int HistogramDevice::prepareChannelCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq,
661                                           uint8_t channelId) {
662     int ret = NO_ERROR;
663     ExynosDisplayDrmInterface *moduleDisplayInterface =
664             static_cast<ExynosDisplayDrmInterface *>(mDisplay->mDisplayInterface.get());
665     if (!moduleDisplayInterface) {
666         HWC_LOGE(mDisplay, "%s: failed to get ExynosDisplayDrmInterface (nullptr)", __func__);
667         return -EINVAL;
668     }
669 
670     ChannelInfo &channel = mChannels[channelId];
671     std::scoped_lock lock(channel.channelInfoMutex);
672 
673     if (channel.status == ChannelStatus_t::CONFIG_COMMITTED ||
674         channel.status == ChannelStatus_t::CONFIG_PENDING) {
675         HistogramRoiRect workingRoi;
676 
677         /* calculate the roi based on the current active resolution */
678         ret = convertRoiLocked(moduleDisplayInterface, channel.requestedRoi, workingRoi);
679         if (ret == -EAGAIN) {
680             /* postpone the histogram config to next atomic commit */
681             ALOGD("%s: histogram channel #%u: set status (CONFIG_PENDING)", __func__, channelId);
682             channel.status = ChannelStatus_t::CONFIG_PENDING;
683             return NO_ERROR;
684         } else if (ret) {
685             ALOGE("%s: histogram channel #%u: failed to convert to workingRoi, ret: %d", __func__,
686                   channelId, ret);
687             channel.status = ChannelStatus_t::CONFIG_ERROR;
688             return ret;
689         }
690 
691         /* If the channel status is CONFIG_COMMITTED, also check if the working roi needs to be
692          * updated due to resolution changed. */
693         if (channel.status == ChannelStatus_t::CONFIG_COMMITTED) {
694             if (LIKELY(channel.workingConfig.roi == workingRoi)) {
695                 return NO_ERROR;
696             } else {
697                 ALOGI("%s: histogram channel #%u: detect resolution changed, update roi setting",
698                       __func__, channelId);
699             }
700         }
701 
702         /* Adopt the roi calculated from convertRoiLocked */
703         channel.workingConfig.roi = workingRoi;
704 
705         /* Create histogram drm config struct (platform dependent) */
706         std::shared_ptr<void> blobData;
707         size_t blobLength = 0;
708         if ((ret = createHistogramDrmConfigLocked(channel, blobData, blobLength))) {
709             ALOGE("%s: histogram channel #%u: failed to createHistogramDrmConfig, ret: %d",
710                   __func__, channelId, ret);
711             channel.status = ChannelStatus_t::CONFIG_ERROR;
712             return ret;
713         }
714 
715         /* Add histogram blob to atomic commit */
716         ret = moduleDisplayInterface->setDisplayHistogramChannelSetting(drmReq, channelId,
717                                                                         blobData.get(), blobLength);
718         if (ret == NO_ERROR) {
719             channel.status = ChannelStatus_t::CONFIG_BLOB_ADDED;
720         } else {
721             ALOGE("%s: histogram channel #%u: failed to setDisplayHistogramChannelSetting, ret: %d",
722                   __func__, channelId, ret);
723             channel.status = ChannelStatus_t::CONFIG_ERROR;
724             return ret;
725         }
726     } else if (channel.status == ChannelStatus_t::DISABLE_PENDING) {
727         ret = moduleDisplayInterface->clearDisplayHistogramChannelSetting(drmReq, channelId);
728         if (ret == NO_ERROR) {
729             channel.status = ChannelStatus_t::DISABLE_BLOB_ADDED;
730         } else {
731             ALOGE("%s: histogram channel #%u: failed to clearDisplayHistogramChannelSetting, ret: "
732                   "%d",
733                   __func__, channelId, ret);
734             channel.status = ChannelStatus_t::DISABLE_ERROR;
735         }
736     }
737 
738     return ret;
739 }
740 
createHistogramDrmConfigLocked(const ChannelInfo & channel,std::shared_ptr<void> & configPtr,size_t & length) const741 int HistogramDevice::createHistogramDrmConfigLocked(const ChannelInfo &channel,
742                                                     std::shared_ptr<void> &configPtr,
743                                                     size_t &length) const {
744     /* Default implementation doesn't know the histogram channel config struct in the kernel.
745      * Cannot allocate and initialize the channel config. */
746     configPtr = nullptr;
747     length = 0;
748     return INVALID_OPERATION;
749 }
750 
convertRoiLocked(ExynosDisplayDrmInterface * moduleDisplayInterface,const HistogramRoiRect & requestedRoi,HistogramRoiRect & workingRoi) const751 int HistogramDevice::convertRoiLocked(ExynosDisplayDrmInterface *moduleDisplayInterface,
752                                       const HistogramRoiRect &requestedRoi,
753                                       HistogramRoiRect &workingRoi) const {
754     int32_t activeH = moduleDisplayInterface->getActiveModeHDisplay();
755     int32_t activeV = moduleDisplayInterface->getActiveModeVDisplay();
756     int32_t panelH = mHistogramCapability.fullResolutionWidth;
757     int32_t panelV = mHistogramCapability.fullResolutionHeight;
758 
759     ALOGV("%s: active: (%dx%d), panel: (%dx%d)", __func__, activeH, activeV, panelH, panelV);
760 
761     if (panelH < activeH || activeH < 0 || panelV < activeV || activeV < 0) {
762         ALOGE("%s: failed to convert roi, active: (%dx%d), panel: (%dx%d)", __func__, activeH,
763               activeV, panelH, panelV);
764         return -EINVAL;
765     } else if (activeH == 0 || activeV == 0) {
766         /* mActiveModeState is not initialized, postpone histogram config to next atomic commit */
767         ALOGW("%s: mActiveModeState is not initialized, active: (%dx%d), postpone histogram config "
768               "to next atomic commit",
769               __func__, activeH, activeV);
770         return -EAGAIN;
771     }
772 
773     /* Linear transform from full resolution to active resolution */
774     workingRoi.left = requestedRoi.left * activeH / panelH;
775     workingRoi.top = requestedRoi.top * activeV / panelV;
776     workingRoi.right = requestedRoi.right * activeH / panelH;
777     workingRoi.bottom = requestedRoi.bottom * activeV / panelV;
778 
779     ALOGV("%s: working roi: %s", __func__, toString(workingRoi).c_str());
780 
781     return NO_ERROR;
782 }
783 
dumpHistogramCapability(String8 & result) const784 void HistogramDevice::dumpHistogramCapability(String8 &result) const {
785     /* Append the histogram capability info to the dump string */
786     result.appendFormat("Histogram capability:\n");
787     result.appendFormat("\tsupportMultiChannel: %s\n",
788                         mHistogramCapability.supportMultiChannel ? "true" : "false");
789     result.appendFormat("\tchannelCount: %d\n", mHistogramCapability.channelCount);
790     result.appendFormat("\tfullscreen roi: (0,0)x(%dx%d)\n",
791                         mHistogramCapability.fullResolutionWidth,
792                         mHistogramCapability.fullResolutionHeight);
793     result.appendFormat("\tsupportSamplePosList:");
794     for (HistogramSamplePos samplePos : mHistogramCapability.supportSamplePosList) {
795         result.appendFormat(" %s",
796                             aidl::com::google::hardware::pixel::display::toString(samplePos)
797                                     .c_str());
798     }
799     result.appendFormat("\n");
800 }
801 
validateHistogramConfig(const HistogramConfig & histogramConfig) const802 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramConfig(
803         const HistogramConfig &histogramConfig) const {
804     HistogramErrorCode ret;
805 
806     if ((ret = validateHistogramRoi(histogramConfig.roi)) != HistogramErrorCode::NONE ||
807         (ret = validateHistogramWeights(histogramConfig.weights)) != HistogramErrorCode::NONE ||
808         (ret = validateHistogramSamplePos(histogramConfig.samplePos)) != HistogramErrorCode::NONE) {
809         return ret;
810     }
811 
812     return HistogramErrorCode::NONE;
813 }
814 
validateHistogramRoi(const HistogramRoiRect & roi) const815 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramRoi(
816         const HistogramRoiRect &roi) const {
817     if ((roi.left < 0) || (roi.top < 0) || (roi.right - roi.left <= 0) ||
818         (roi.bottom - roi.top <= 0) || (roi.right > mHistogramCapability.fullResolutionWidth) ||
819         (roi.bottom > mHistogramCapability.fullResolutionHeight)) {
820         ALOGE("%s: BAD_ROI, roi: %s, full screen roi: (0,0)x(%dx%d)", __func__,
821               toString(roi).c_str(), mHistogramCapability.fullResolutionWidth,
822               mHistogramCapability.fullResolutionHeight);
823         return HistogramErrorCode::BAD_ROI;
824     }
825 
826     return HistogramErrorCode::NONE;
827 }
828 
validateHistogramWeights(const HistogramWeights & weights) const829 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramWeights(
830         const HistogramWeights &weights) const {
831     if ((weights.weightR + weights.weightG + weights.weightB) != WEIGHT_SUM) {
832         ALOGE("%s: BAD_WEIGHT, weights(%d,%d,%d)\n", __func__, weights.weightR, weights.weightG,
833               weights.weightB);
834         return HistogramErrorCode::BAD_WEIGHT;
835     }
836 
837     return HistogramErrorCode::NONE;
838 }
839 
validateHistogramSamplePos(const HistogramSamplePos & samplePos) const840 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramSamplePos(
841         const HistogramSamplePos &samplePos) const {
842     for (HistogramSamplePos mSamplePos : mHistogramCapability.supportSamplePosList) {
843         if (samplePos == mSamplePos) {
844             return HistogramErrorCode::NONE;
845         }
846     }
847 
848     ALOGE("%s: BAD_POSITION, samplePos is %d", __func__, (int)samplePos);
849     return HistogramErrorCode::BAD_POSITION;
850 }
851 
calculateThreshold(const HistogramRoiRect & roi)852 int HistogramDevice::calculateThreshold(const HistogramRoiRect &roi) {
853     // TODO: b/294491895 - Check if the threshold plus one really need it
854     int threshold = ((roi.bottom - roi.top) * (roi.right - roi.left)) >> 16;
855     return threshold + 1;
856 }
857 
toString(const ChannelStatus_t & status)858 std::string HistogramDevice::toString(const ChannelStatus_t &status) {
859     switch (status) {
860         case ChannelStatus_t::RESERVED:
861             return "RESERVED";
862         case ChannelStatus_t::DISABLED:
863             return "DISABLED";
864         case ChannelStatus_t::CONFIG_PENDING:
865             return "CONFIG_PENDING";
866         case ChannelStatus_t::CONFIG_BLOB_ADDED:
867             return "CONFIG_BLOB_ADDED";
868         case ChannelStatus_t::CONFIG_COMMITTED:
869             return "CONFIG_COMMITTED";
870         case ChannelStatus_t::CONFIG_ERROR:
871             return "CONFIG_ERROR";
872         case ChannelStatus_t::DISABLE_PENDING:
873             return "DISABLE_PENDING";
874         case ChannelStatus_t::DISABLE_BLOB_ADDED:
875             return "DISABLE_BLOB_ADDED";
876         case ChannelStatus_t::DISABLE_ERROR:
877             return "DISABLE_ERROR";
878     }
879 
880     return "UNDEFINED";
881 }
882 
toString(const HistogramRoiRect & roi)883 std::string HistogramDevice::toString(const HistogramRoiRect &roi) {
884     std::ostringstream os;
885     os << "(" << roi.left << "," << roi.top << ")";
886     os << "x";
887     os << "(" << roi.right << "," << roi.bottom << ")";
888     return os.str();
889 }
890 
toString(const HistogramWeights & weights)891 std::string HistogramDevice::toString(const HistogramWeights &weights) {
892     std::ostringstream os;
893     os << "(";
894     os << (int)weights.weightR << ",";
895     os << (int)weights.weightG << ",";
896     os << (int)weights.weightB;
897     os << ")";
898     return os.str();
899 }
900