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