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 #include "astro-display.h"
5 #include <fbl/auto_call.h>
6 #include <ddk/platform-defs.h>
7
8 namespace astro_display {
9
10 namespace {
11 // List of supported pixel formats
12 zx_pixel_format_t kSupportedPixelFormats[] = { ZX_PIXEL_FORMAT_RGB_x888 };
13
14 constexpr uint64_t kDisplayId = PANEL_DISPLAY_ID;
15
16 // Astro Display Configuration. These configuration comes directly from
17 // from LCD vendor and hardware team.
18 constexpr DisplaySetting kDisplaySettingTV070WSM_FT = {
19 .lane_num = 4,
20 .bit_rate_max = 360,
21 .clock_factor = 8,
22 .lcd_clock = 44250000,
23 .h_active = 600,
24 .v_active = 1024,
25 .h_period = 700,
26 .v_period = 1053,
27 .hsync_width = 24,
28 .hsync_bp = 36,
29 .hsync_pol = 0,
30 .vsync_width = 2,
31 .vsync_bp = 8,
32 .vsync_pol = 0,
33 };
34 constexpr DisplaySetting kDisplaySettingP070ACB_FT = {
35 .lane_num = 4,
36 .bit_rate_max = 400,
37 .clock_factor = 8,
38 .lcd_clock = 49434000,
39 .h_active = 600,
40 .v_active = 1024,
41 .h_period = 770,
42 .v_period = 1070,
43 .hsync_width = 10,
44 .hsync_bp = 80,
45 .hsync_pol = 0,
46 .vsync_width = 6,
47 .vsync_bp = 20,
48 .vsync_pol = 0,
49 };
50
51 } // namespace
52
53 // This function copies the display settings into our internal structure
CopyDisplaySettings()54 void AstroDisplay::CopyDisplaySettings() {
55 ZX_DEBUG_ASSERT(init_disp_table_);
56
57 disp_setting_.h_active = init_disp_table_->h_active;
58 disp_setting_.v_active = init_disp_table_->v_active;
59 disp_setting_.h_period = init_disp_table_->h_period;
60 disp_setting_.v_period = init_disp_table_->v_period;
61 disp_setting_.hsync_width = init_disp_table_->hsync_width;
62 disp_setting_.hsync_bp = init_disp_table_->hsync_bp;
63 disp_setting_.hsync_pol = init_disp_table_->hsync_pol;
64 disp_setting_.vsync_width = init_disp_table_->vsync_width;
65 disp_setting_.vsync_bp = init_disp_table_->vsync_bp;
66 disp_setting_.vsync_pol = init_disp_table_->vsync_pol;
67 disp_setting_.lcd_clock = init_disp_table_->lcd_clock;
68 disp_setting_.clock_factor = init_disp_table_->clock_factor;
69 disp_setting_.lane_num = init_disp_table_->lane_num;
70 disp_setting_.bit_rate_max = init_disp_table_->bit_rate_max;
71 }
72
PopulateAddedDisplayArgs(added_display_args_t * args)73 void AstroDisplay::PopulateAddedDisplayArgs(added_display_args_t* args) {
74 args->display_id = kDisplayId;
75 args->edid_present = false;
76 args->panel.params.height = height_;
77 args->panel.params.width = width_;
78 args->panel.params.refresh_rate_e2 = 3000; // Just guess that it's 30fps
79 args->pixel_format_list = kSupportedPixelFormats;
80 args->pixel_format_count = countof(kSupportedPixelFormats);
81 args->cursor_info_count = 0;
82 }
83
84 // part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
DisplayControllerImplComputeLinearStride(uint32_t width,zx_pixel_format_t format)85 uint32_t AstroDisplay::DisplayControllerImplComputeLinearStride(uint32_t width,
86 zx_pixel_format_t format) {
87 // The astro display controller needs buffers with a stride that is an even
88 // multiple of 32.
89 return ROUNDUP(width, 32 / ZX_PIXEL_FORMAT_BYTES(format));
90 }
91
92 // part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
DisplayControllerImplSetDisplayControllerInterface(const display_controller_interface_t * intf)93 void AstroDisplay::DisplayControllerImplSetDisplayControllerInterface(
94 const display_controller_interface_t* intf) {
95 fbl::AutoLock lock(&display_lock_);
96 dc_intf_ = ddk::DisplayControllerInterfaceClient(intf);
97 added_display_args_t args;
98 PopulateAddedDisplayArgs(&args);
99 dc_intf_.OnDisplaysChanged(&args, 1, nullptr, 0, nullptr, 0, nullptr);
100 }
101
102 // part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
DisplayControllerImplImportVmoImage(image_t * image,zx::vmo vmo,size_t offset)103 zx_status_t AstroDisplay::DisplayControllerImplImportVmoImage(image_t* image, zx::vmo vmo,
104 size_t offset) {
105 zx_status_t status = ZX_OK;
106 fbl::AutoLock lock(&image_lock_);
107
108 if (image->type != IMAGE_TYPE_SIMPLE || image->pixel_format != format_) {
109 status = ZX_ERR_INVALID_ARGS;
110 return status;
111 }
112
113 uint32_t stride = DisplayControllerImplComputeLinearStride(image->width, image->pixel_format);
114
115 canvas_info_t canvas_info;
116 canvas_info.height = image->height;
117 canvas_info.stride_bytes = stride * ZX_PIXEL_FORMAT_BYTES(image->pixel_format);
118 canvas_info.wrap = 0;
119 canvas_info.blkmode = 0;
120 canvas_info.endianness = 0;
121
122 uint8_t local_canvas_idx;
123 status = amlogic_canvas_config(&canvas_, vmo.release(), offset, &canvas_info,
124 &local_canvas_idx);
125 if (status != ZX_OK) {
126 DISP_ERROR("Could not configure canvas: %d\n", status);
127 status = ZX_ERR_NO_RESOURCES;
128 return status;
129 }
130 if (imported_images_.GetOne(local_canvas_idx)) {
131 DISP_INFO("Reusing previously allocated canvas (index = %d)\n", local_canvas_idx);
132 }
133 imported_images_.SetOne(local_canvas_idx);
134 image->handle = local_canvas_idx;
135
136 return status;
137 }
138
139 // part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
DisplayControllerImplReleaseImage(image_t * image)140 void AstroDisplay::DisplayControllerImplReleaseImage(image_t* image) {
141 fbl::AutoLock lock(&image_lock_);
142 size_t local_canvas_idx = (size_t)image->handle;
143 if (imported_images_.GetOne(local_canvas_idx)) {
144 imported_images_.ClearOne(local_canvas_idx);
145 amlogic_canvas_free(&canvas_, static_cast<uint8_t>(local_canvas_idx));
146 }
147 }
148
149 // part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
DisplayControllerImplCheckConfiguration(const display_config_t ** display_configs,size_t display_count,uint32_t ** layer_cfg_results,size_t * layer_cfg_result_count)150 uint32_t AstroDisplay::DisplayControllerImplCheckConfiguration(
151 const display_config_t** display_configs, size_t display_count, uint32_t** layer_cfg_results,
152 size_t* layer_cfg_result_count) {
153
154 if (display_count != 1) {
155 ZX_DEBUG_ASSERT(display_count == 0);
156 return CONFIG_DISPLAY_OK;
157 }
158 ZX_DEBUG_ASSERT(display_configs[0]->display_id == PANEL_DISPLAY_ID);
159
160 fbl::AutoLock lock(&display_lock_);
161
162 bool success;
163 if (display_configs[0]->layer_count != 1) {
164 success = display_configs[0]->layer_count == 0;
165 } else {
166 const primary_layer_t& layer = display_configs[0]->layer_list[0]->cfg.primary;
167 frame_t frame = {
168 .x_pos = 0, .y_pos = 0, .width = width_, .height = height_,
169 };
170 success = display_configs[0]->layer_list[0]->type == LAYER_TYPE_PRIMARY
171 && layer.transform_mode == FRAME_TRANSFORM_IDENTITY
172 && layer.image.width == width_
173 && layer.image.height == height_
174 && memcmp(&layer.dest_frame, &frame, sizeof(frame_t)) == 0
175 && memcmp(&layer.src_frame, &frame, sizeof(frame_t)) == 0
176 && display_configs[0]->cc_flags == 0
177 && layer.alpha_mode == ALPHA_DISABLE;
178 }
179 if (!success) {
180 layer_cfg_results[0][0] = CLIENT_MERGE_BASE;
181 for (unsigned i = 1; i < display_configs[0]->layer_count; i++) {
182 layer_cfg_results[0][i] = CLIENT_MERGE_SRC;
183 }
184 layer_cfg_result_count[0] = display_configs[0]->layer_count;
185 }
186 return CONFIG_DISPLAY_OK;
187 }
188
189 // part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
DisplayControllerImplApplyConfiguration(const display_config_t ** display_configs,size_t display_count)190 void AstroDisplay::DisplayControllerImplApplyConfiguration( const display_config_t** display_configs,
191 size_t display_count) {
192 ZX_DEBUG_ASSERT(display_configs);
193
194 fbl::AutoLock lock(&display_lock_);
195
196 uint8_t addr;
197 if (display_count == 1 && display_configs[0]->layer_count) {
198 // Since Astro does not support plug'n play (fixed display), there is no way
199 // a checked configuration could be invalid at this point.
200 addr = (uint8_t) (uint64_t) display_configs[0]->layer_list[0]->cfg.primary.image.handle;
201 current_image_valid_= true;
202 current_image_ = addr;
203 osd_->FlipOnVsync(addr);
204 } else {
205 current_image_valid_= false;
206 osd_->Disable();
207 }
208 }
209
210 // part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
DisplayControllerImplAllocateVmo(uint64_t size,zx::vmo * vmo_out)211 zx_status_t AstroDisplay::DisplayControllerImplAllocateVmo(uint64_t size, zx::vmo* vmo_out) {
212 return zx::vmo::create_contiguous(bti_, size, 0, vmo_out);
213 }
214
DdkUnbind()215 void AstroDisplay::DdkUnbind() {
216 DdkRemove();
217 }
218
DdkRelease()219 void AstroDisplay::DdkRelease() {
220 if (osd_) {
221 osd_->Release();
222 }
223 vsync_irq_.destroy();
224 thrd_join(vsync_thread_, NULL);
225 delete this;
226 }
227
228 // This function detect the panel type based.
PopulatePanelType()229 void AstroDisplay::PopulatePanelType() {
230 uint8_t pt;
231 if ((gpio_config_in(&gpio_, GPIO_NO_PULL) == ZX_OK) &&
232 (gpio_read(&gpio_, &pt) == ZX_OK)) {
233 panel_type_ = pt;
234 DISP_INFO("Detected panel type = %s (%d)\n",
235 panel_type_ ? "P070ACB_FT" : "TV070WSM_FT", panel_type_);
236 } else {
237 panel_type_ = PANEL_UNKNOWN;
238 DISP_ERROR("Failed to detect a valid panel\n");
239 }
240 }
241
SetupDisplayInterface()242 zx_status_t AstroDisplay::SetupDisplayInterface() {
243 zx_status_t status;
244 fbl::AutoLock lock(&display_lock_);
245
246 // Figure out board rev and panel type for Astro only
247 if (board_info_.pid == PDEV_PID_ASTRO) {
248 //TODO(payamm): set to true for now until we figure out bootloader to userspace handoff
249 skip_disp_init_ = true;
250 panel_type_ = PANEL_UNKNOWN;
251
252 if (board_info_.board_revision < BOARD_REV_EVT_1) {
253 DISP_INFO("Unsupported Board REV (%d). Will skip display driver initialization\n",
254 board_info_.board_revision);
255 skip_disp_init_ = true;
256 }
257 } else {
258 skip_disp_init_ = true;
259 }
260
261 if (!skip_disp_init_) {
262 // Detect panel type
263 PopulatePanelType();
264
265 if (panel_type_ == PANEL_TV070WSM_FT) {
266 init_disp_table_ = &kDisplaySettingTV070WSM_FT;
267 } else if (panel_type_ == PANEL_P070ACB_FT) {
268 init_disp_table_ = &kDisplaySettingP070ACB_FT;
269 } else {
270 DISP_ERROR("Unsupported panel detected!\n");
271 status = ZX_ERR_NOT_SUPPORTED;
272 return status;
273 }
274
275 // Populated internal structures based on predefined tables
276 CopyDisplaySettings();
277 }
278
279 format_ = ZX_PIXEL_FORMAT_RGB_x888;
280 stride_ = DisplayControllerImplComputeLinearStride(width_, format_);
281
282 if (!skip_disp_init_) {
283 // Ensure Max Bit Rate / pixel clock ~= 8 (8.xxx). This is because the clock calculation
284 // part of code assumes a clock factor of 1. All the LCD tables from Astro have this
285 // relationship established. We'll have to revisit the calculation if this ratio cannot
286 // be met.
287 if (init_disp_table_->bit_rate_max / (init_disp_table_->lcd_clock / 1000 / 1000) != 8) {
288 DISP_ERROR("Max Bit Rate / pixel clock != 8\n");
289 status = ZX_ERR_INVALID_ARGS;
290 return status;
291 }
292
293 // Setup VPU and VPP units first
294 fbl::AllocChecker ac;
295 vpu_ = fbl::make_unique_checked<astro_display::Vpu>(&ac);
296 if (!ac.check()) {
297 return ZX_ERR_NO_MEMORY;
298 }
299 status = vpu_->Init(parent_);
300 if (status != ZX_OK) {
301 DISP_ERROR("Could not initialize VPU object\n");
302 return status;
303 }
304 vpu_->PowerOff();
305 vpu_->PowerOn();
306 vpu_->VppInit();
307
308 clock_ = fbl::make_unique_checked<astro_display::AstroDisplayClock>(&ac);
309 if (!ac.check()) {
310 return ZX_ERR_NO_MEMORY;
311 }
312 status = clock_->Init(parent_);
313 if (status != ZX_OK) {
314 DISP_ERROR("Could not initialize Clock object\n");
315 return status;
316 }
317
318 // Enable all display related clocks
319 status = clock_->Enable(disp_setting_);
320 if (status != ZX_OK) {
321 DISP_ERROR("Could not enable display clocks!\n");
322 return status;
323 }
324
325 // Program and Enable DSI Host Interface
326 dsi_host_ = fbl::make_unique_checked<astro_display::AmlDsiHost>(&ac,
327 parent_,
328 clock_->GetBitrate(),
329 panel_type_);
330 if (!ac.check()) {
331 return ZX_ERR_NO_MEMORY;
332 }
333 status = dsi_host_->Init();
334 if (status != ZX_OK) {
335 DISP_ERROR("Could not initialize DSI Host\n");
336 return status;
337 }
338
339 status = dsi_host_->HostOn(disp_setting_);
340 if (status != ZX_OK) {
341 DISP_ERROR("DSI Host On failed! %d\n", status);
342 return status;
343 }
344 }
345
346 /// OSD
347 // Create internal osd object
348 fbl::AllocChecker ac;
349 osd_ = fbl::make_unique_checked<astro_display::Osd>(&ac,
350 width_,
351 height_,
352 disp_setting_.h_active,
353 disp_setting_.v_active);
354 if (!ac.check()) {
355 return ZX_ERR_NO_MEMORY;
356 }
357 // Initialize osd object
358 status = osd_->Init(parent_);
359 if (status != ZX_OK) {
360 DISP_ERROR("Could not initialize OSD object\n");
361 return status;
362 }
363
364 if (!skip_disp_init_) {
365 osd_->HwInit();
366 }
367
368 // Configure osd layer
369 current_image_valid_= false;
370 status = osd_->Configure();
371 if (status != ZX_OK) {
372 DISP_ERROR("OSD configuration failed!\n");
373 return status;
374 }
375
376 {
377 // Reset imported_images_ bitmap
378 fbl::AutoLock lock(&image_lock_);
379 imported_images_.Reset(kMaxImportedImages);
380 }
381
382 if (dc_intf_.is_valid()) {
383 added_display_args_t args;
384 PopulateAddedDisplayArgs(&args);
385 dc_intf_.OnDisplaysChanged(&args, 1, nullptr, 0, nullptr, 0, nullptr);
386 }
387
388 return ZX_OK;
389 }
390
VSyncThread()391 int AstroDisplay::VSyncThread() {
392 zx_status_t status;
393 while (1) {
394 status = vsync_irq_.wait(nullptr);
395 if (status != ZX_OK) {
396 DISP_ERROR("VSync Interrupt Wait failed\n");
397 break;
398 }
399 fbl::AutoLock lock(&display_lock_);
400 uint64_t live[] = { current_image_ };
401 bool current_image_valid = current_image_valid_;
402 if (dc_intf_.is_valid()) {
403 dc_intf_.OnDisplayVsync(kDisplayId, zx_clock_get(ZX_CLOCK_MONOTONIC),
404 live, current_image_valid);
405 }
406 }
407
408 return status;
409 }
410
411 // TODO(payamm): make sure unbind/release are called if we return error
Bind()412 zx_status_t AstroDisplay::Bind() {
413 zx_status_t status;
414
415 status = device_get_protocol(parent_, ZX_PROTOCOL_PDEV, &pdev_);
416 if (status != ZX_OK) {
417 DISP_ERROR("Could not get parent protocol\n");
418 return status;
419 }
420
421 // Get board info
422 status = pdev_get_board_info(&pdev_, &board_info_);
423 if (status != ZX_OK) {
424 DISP_ERROR("Could not obtain board info\n");
425 return status;
426 }
427
428 if (board_info_.pid == PDEV_PID_ASTRO) {
429 // Obtain GPIO Protocol for Panel reset
430 size_t actual;
431 status = pdev_get_protocol(&pdev_, ZX_PROTOCOL_GPIO, GPIO_PANEL_DETECT, &gpio_, sizeof(gpio_),
432 &actual);
433 if (status != ZX_OK) {
434 DISP_ERROR("Could not obtain GPIO protocol.\n");
435 return status;
436 }
437 }
438
439 if (board_info_.pid == PDEV_PID_SHERLOCK) {
440 width_ = SHERLOCK_DISPLAY_WIDTH;
441 height_ = SHERLOCK_DISPLAY_HEIGHT;
442 }
443
444 status = device_get_protocol(parent_, ZX_PROTOCOL_AMLOGIC_CANVAS, &canvas_);
445 if (status != ZX_OK) {
446 DISP_ERROR("Could not obtain CANVAS protocol\n");
447 return status;
448 }
449
450 status = pdev_get_bti(&pdev_, 0, bti_.reset_and_get_address());
451 if (status != ZX_OK) {
452 DISP_ERROR("Could not get BTI handle\n");
453 return status;
454 }
455
456 // Setup Display Interface
457 status = SetupDisplayInterface();
458 if (status != ZX_OK) {
459 DISP_ERROR("Astro display setup failed! %d\n", status);
460 return status;
461 }
462
463 // Map VSync Interrupt
464 status = pdev_map_interrupt(&pdev_, IRQ_VSYNC, vsync_irq_.reset_and_get_address());
465 if (status != ZX_OK) {
466 DISP_ERROR("Could not map vsync interrupt\n");
467 return status;
468 }
469
470 auto start_thread = [](void* arg) { return static_cast<AstroDisplay*>(arg)->VSyncThread(); };
471 status = thrd_create_with_name(&vsync_thread_, start_thread, this, "vsync_thread");
472 if (status != ZX_OK) {
473 DISP_ERROR("Could not create vsync_thread\n");
474 return status;
475 }
476
477 auto cleanup = fbl::MakeAutoCall([&]() { DdkRelease(); });
478
479 status = DdkAdd("astro-display");
480 if (status != ZX_OK) {
481 DISP_ERROR("Could not add device\n");
482 return status;
483 }
484
485 cleanup.cancel();
486 return ZX_OK;
487 }
488
Dump()489 void AstroDisplay::Dump() {
490 DISP_INFO("#############################\n");
491 DISP_INFO("Dumping disp_setting structure:\n");
492 DISP_INFO("#############################\n");
493 DISP_INFO("h_active = 0x%x (%u)\n", disp_setting_.h_active,
494 disp_setting_.h_active);
495 DISP_INFO("v_active = 0x%x (%u)\n", disp_setting_.v_active,
496 disp_setting_.v_active);
497 DISP_INFO("h_period = 0x%x (%u)\n", disp_setting_.h_period,
498 disp_setting_.h_period);
499 DISP_INFO("v_period = 0x%x (%u)\n", disp_setting_.v_period,
500 disp_setting_.v_period);
501 DISP_INFO("hsync_width = 0x%x (%u)\n", disp_setting_.hsync_width,
502 disp_setting_.hsync_width);
503 DISP_INFO("hsync_bp = 0x%x (%u)\n", disp_setting_.hsync_bp,
504 disp_setting_.hsync_bp);
505 DISP_INFO("hsync_pol = 0x%x (%u)\n", disp_setting_.hsync_pol,
506 disp_setting_.hsync_pol);
507 DISP_INFO("vsync_width = 0x%x (%u)\n", disp_setting_.vsync_width,
508 disp_setting_.vsync_width);
509 DISP_INFO("vsync_bp = 0x%x (%u)\n", disp_setting_.vsync_bp,
510 disp_setting_.vsync_bp);
511 DISP_INFO("vsync_pol = 0x%x (%u)\n", disp_setting_.vsync_pol,
512 disp_setting_.vsync_pol);
513 DISP_INFO("lcd_clock = 0x%x (%u)\n", disp_setting_.lcd_clock,
514 disp_setting_.lcd_clock);
515 DISP_INFO("lane_num = 0x%x (%u)\n", disp_setting_.lane_num,
516 disp_setting_.lane_num);
517 DISP_INFO("bit_rate_max = 0x%x (%u)\n", disp_setting_.bit_rate_max,
518 disp_setting_.bit_rate_max);
519 DISP_INFO("clock_factor = 0x%x (%u)\n", disp_setting_.clock_factor,
520 disp_setting_.clock_factor);
521 }
522
523 } // namespace astro_display
524
525 // main bind function called from dev manager
astro_display_bind(void * ctx,zx_device_t * parent)526 extern "C" zx_status_t astro_display_bind(void* ctx, zx_device_t* parent) {
527 fbl::AllocChecker ac;
528 auto dev = fbl::make_unique_checked<astro_display::AstroDisplay>(&ac,
529 parent, DISPLAY_WIDTH, DISPLAY_HEIGHT);
530 if (!ac.check()) {
531 return ZX_ERR_NO_MEMORY;
532 }
533
534 auto status = dev->Bind();
535 if (status == ZX_OK) {
536 // devmgr is now in charge of the memory for dev
537 __UNUSED auto ptr = dev.release();
538 }
539 return status;
540 }
541