608 lines
17 KiB
C
608 lines
17 KiB
C
/*
|
|
* Copyright (C) 2009-2015 Apple Inc. All rights reserved.
|
|
*
|
|
* This document is the property of Apple Inc.
|
|
* It is considered confidential and proprietary.
|
|
*
|
|
* This document may not be reproduced or transmitted in any form,
|
|
* in whole or in part, without the express written permission of
|
|
* Apple Inc.
|
|
*/
|
|
#include <debug.h>
|
|
#include <assert.h>
|
|
#include <drivers/display.h>
|
|
#include <lib/env.h>
|
|
#include <lib/paint.h>
|
|
#include <platform.h>
|
|
#include <platform/chipid.h>
|
|
#include <platform/clocks.h>
|
|
#include <platform/int.h>
|
|
#include <platform/memmap.h>
|
|
#include <platform/soc/hwclocks.h>
|
|
#include <platform/soc/hwisr.h>
|
|
#include <target.h>
|
|
#include <sys.h>
|
|
#include <sys/task.h>
|
|
|
|
//Front End
|
|
#if WITH_DFE_ADFE
|
|
# include <drivers/adfe/adfe.h>
|
|
#elif WITH_DFE_ADFE_V2
|
|
# include <drivers/adfe_v2/adfe.h>
|
|
#else
|
|
# error "unknown displayfrontend"
|
|
#endif // WITH_DFE_ADFE
|
|
|
|
//Back End
|
|
#if WITH_DBE_CLCD
|
|
# include <drivers/clcd_v2/clcd.h>
|
|
#elif WITH_DBE_RGBOUT
|
|
# include <drivers/rgbout/rgbout.h>
|
|
#elif WITH_DBE_ADBE
|
|
# include <drivers/adbe/adbe.h>
|
|
#else
|
|
# error "unknown display-backend"
|
|
#endif
|
|
|
|
//Transport
|
|
#if WITH_HW_DISPLAY_HDMI
|
|
#include <drivers/hdmi.h>
|
|
#elif (WITH_HW_DISPLAY_EDP || WITH_HW_DISPLAY_DISPLAYPORT)
|
|
#include <drivers/displayport/displayport.h>
|
|
#include <drivers/displayport.h>
|
|
#endif
|
|
|
|
#include <platform/soc/display_timings.h>
|
|
|
|
#ifndef LINEAR_STRIDE_ALIGNMENT
|
|
#define LINEAR_STRIDE_ALIGNMENT 64
|
|
#endif
|
|
|
|
#define LINEAR_STRIDE_ALIGNMENT_MASK (LINEAR_STRIDE_ALIGNMENT - 1)
|
|
|
|
#ifndef TARGET_FB_MULT
|
|
#define TARGET_FB_MULT 30
|
|
#endif
|
|
|
|
static u_int32_t display_default_color;
|
|
static u_int64_t display_frame_us = 16667;
|
|
static bool display_enabled;
|
|
static bool display_enabled_valid;
|
|
|
|
static struct display_window *main_window;
|
|
static u_int32_t *red_lut = NULL, *green_lut = NULL, *blue_lut = NULL;
|
|
|
|
static struct display_timing *timing_info;
|
|
|
|
static const int32_t timing_list_size = sizeof(timing_list) / sizeof(struct display_timing);
|
|
|
|
static void display_backend_enable_clocks(bool enable);
|
|
static void display_backend_init(struct display_timing *timing);
|
|
static void display_backend_enable_timing_generator(bool enable);
|
|
static void display_backend_install_gamma_table(u_int32_t *red_lut, u_int32_t *green_lut, u_int32_t *blue_lut, struct syscfg_wpcl *wpcl);
|
|
static int display_set_timings(struct display_timing *timing);
|
|
static void display_set_timing_info();
|
|
|
|
static void display_set_timing_info()
|
|
{
|
|
const char *env;
|
|
int32_t cnt;
|
|
|
|
if (timing_info != NULL)
|
|
return;
|
|
|
|
env = env_get("display-timing");
|
|
if (env == 0) env = "";
|
|
|
|
for (cnt = 0; cnt < timing_list_size; cnt++) {
|
|
if (strcmp(env, timing_list[cnt].display_name)) continue;
|
|
|
|
timing_info = timing_list + cnt;
|
|
timing_info->display_config = target_get_display_configuration();
|
|
}
|
|
}
|
|
|
|
int display_init(void)
|
|
{
|
|
const char *env;
|
|
enum colorspace color = CS_4BPP;
|
|
int result;
|
|
u_int32_t display_id;
|
|
int32_t cnt, stride;
|
|
struct syscfg_wpcl display_wpcl;
|
|
u_int64_t tmp = 1;
|
|
|
|
dprintf(DEBUG_SPEW, "display_init()\n");
|
|
|
|
/* clear the display memory */
|
|
bzero((void *)DISPLAY_BASE, DISPLAY_SIZE);
|
|
|
|
display_set_timing_info();
|
|
|
|
if (timing_info == 0) {
|
|
dprintf(DEBUG_INFO, "Failed to find display timing info, bailing display_init()\n");
|
|
return -1;
|
|
}
|
|
|
|
// Make sure clocks (both display front-end and back-end) are on
|
|
display_backend_enable_clocks(true);
|
|
|
|
display_set_timings(timing_info);
|
|
|
|
adfe_init(timing_info);
|
|
|
|
// Setup back-end (includes dither, color-manager)
|
|
display_backend_init(timing_info);
|
|
|
|
if (timing_info->pixel_clock) {
|
|
// Calculate display frame interval (in usecs) based on timing info
|
|
tmp = 1000000ULL;
|
|
tmp *= (timing_info->v_back_porch + timing_info->v_front_porch + timing_info->v_pulse_width + timing_info->v_active);
|
|
tmp *= (timing_info->h_back_porch + timing_info->h_front_porch + timing_info->h_pulse_width + timing_info->h_active);
|
|
tmp /= timing_info->pixel_clock;
|
|
}
|
|
|
|
display_frame_us = tmp;
|
|
env = env_get("display-color-space");
|
|
if (env == 0) env = "RGB888";
|
|
|
|
if (!strcmp(env, "RGB888")) color = CS_RGB888;
|
|
else if (!strcmp(env, "RGB565")) color = CS_RGB565;
|
|
else if (!strcmp(env, "ARGB8101010")) color = CS_ARGB8101010;
|
|
|
|
main_window = display_create_window(0, 0, timing_info->h_active, timing_info->v_active, color);
|
|
if (main_window == 0) return -1;
|
|
|
|
/* add an environment variable with the address of the framebuffer */
|
|
env_set_uint("framebuffer", (uintptr_t)main_window->c.fb_phys, 0);
|
|
|
|
// request the desired pixel clock
|
|
clock_set_frequency(timing_info->host_clock_id, 0, 0, 0, 0, timing_info->pixel_clock);
|
|
|
|
switch (timing_info->display_type) {
|
|
#if WITH_HW_DISPLAY_PINOT
|
|
case DISPLAY_TYPE_PINOT :
|
|
{
|
|
extern int pinot_init(struct display_timing *timing, enum colorspace color, u_int32_t *display_id);
|
|
result = pinot_init(timing_info, main_window->cs, &display_id);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#if WITH_HW_DISPLAY_SUMMIT
|
|
case DISPLAY_TYPE_SUMMIT :
|
|
{
|
|
extern int summit_init(struct display_timing *timing, enum colorspace color, u_int32_t *display_id);
|
|
result = summit_init(timing_info, main_window->cs, &display_id);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#if WITH_HW_DISPLAY_EDP
|
|
case DISPLAY_TYPE_EDP :
|
|
{
|
|
extern int edp_init(struct display_timing *timing, enum colorspace color, u_int32_t *display_id);
|
|
result = edp_init(timing_info, main_window->cs, &display_id);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#if (WITH_HW_DISPLAY_DISPLAYPORT && !WITH_HW_MCU)
|
|
|
|
case DISPLAY_TYPE_DP :
|
|
{
|
|
result = displayport_init_with_timing_info(timing_info);
|
|
break;
|
|
}
|
|
#endif
|
|
#if WITH_HW_DISPLAY_HDMI
|
|
case DISPLAY_TYPE_HDMI :
|
|
{
|
|
result = hdmi_init_with_timing_info(timing_info);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
default :
|
|
display_id = 0;
|
|
result = 0;
|
|
}
|
|
|
|
if (result == 0) {
|
|
if (!display_enabled_valid)
|
|
display_enabled = display_get_enable();
|
|
|
|
if (!display_enabled) {
|
|
display_set_enable(true, &display_default_color);
|
|
}
|
|
|
|
red_lut = (u_int32_t *)malloc(257 * sizeof(u_int32_t));
|
|
green_lut = (u_int32_t *)malloc(257 * sizeof(u_int32_t));
|
|
blue_lut = (u_int32_t *)malloc(257 * sizeof(u_int32_t));
|
|
|
|
// Generate linear LUT
|
|
stride = 256 >> (10 - (timing_info->display_depth / 3));
|
|
red_lut[0] = 1;
|
|
for (cnt = 1; cnt < 257; cnt++) {
|
|
if ((cnt % stride) == 1) red_lut[cnt - 1]--;
|
|
red_lut[cnt] = red_lut[cnt - 1] + 4;
|
|
green_lut[cnt - 1] = blue_lut[cnt - 1] = red_lut[cnt - 1];
|
|
}
|
|
green_lut[cnt - 1] = blue_lut[cnt - 1] = red_lut[cnt - 1];
|
|
|
|
#if PRODUCT_IBOOT
|
|
paint_install_gamma_table(display_id, 257, red_lut, green_lut, blue_lut);
|
|
#endif
|
|
paint_get_syscfg_wpcl(&display_wpcl);
|
|
dprintf(DEBUG_SPEW, "paint_install_gamma_table: Found WpCl version = 0x%08x, red = 0x%08x, green = 0x%08x, blue = 0x%08x\n", display_wpcl.version, display_wpcl.red, display_wpcl.green, display_wpcl.blue);
|
|
|
|
display_backend_install_gamma_table(red_lut, green_lut, blue_lut, &display_wpcl);
|
|
|
|
} else {
|
|
display_set_enable(false, NULL);
|
|
timing_info = NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void display_clear(void)
|
|
{
|
|
if (main_window) {
|
|
fill_rect(&main_window->c, 0, 0, main_window->width, main_window->height, display_default_color);
|
|
}
|
|
}
|
|
|
|
|
|
int display_quiesce(bool clear_display)
|
|
{
|
|
int result = 0;
|
|
|
|
//enable clocks
|
|
display_backend_enable_clocks(true);
|
|
if (!display_get_enable()) {
|
|
//disable the clock
|
|
display_backend_enable_clocks(false);
|
|
return ENXIO;
|
|
}
|
|
|
|
display_set_timing_info();
|
|
|
|
if (timing_info == NULL) return result;
|
|
|
|
if (clear_display) {
|
|
display_clear();
|
|
|
|
display_delay_frames(2);
|
|
}
|
|
adfe_disable_error_handler();
|
|
|
|
if (red_lut)
|
|
free(red_lut);
|
|
if (green_lut)
|
|
free(green_lut);
|
|
if (blue_lut)
|
|
free(blue_lut);
|
|
|
|
red_lut = NULL;
|
|
green_lut = NULL;
|
|
blue_lut = NULL;
|
|
|
|
#if WITH_HW_DISPLAY_PINOT
|
|
if (timing_info->display_type == DISPLAY_TYPE_PINOT) {
|
|
extern int pinot_quiesce(void);
|
|
result = pinot_quiesce();
|
|
if (result != 0) return result;
|
|
}
|
|
#endif
|
|
|
|
#if WITH_HW_DISPLAY_SUMMIT
|
|
if (timing_info->display_type == DISPLAY_TYPE_SUMMIT) {
|
|
extern int summit_quiesce(void);
|
|
result = summit_quiesce();
|
|
if (result != 0) return result;
|
|
}
|
|
#endif
|
|
|
|
#if WITH_HW_DISPLAY_EDP
|
|
if (timing_info->display_type == DISPLAY_TYPE_EDP) {
|
|
extern int edp_quiesce(void);
|
|
result = edp_quiesce();
|
|
result = 0;
|
|
if (result != 0) return result;
|
|
}
|
|
#endif
|
|
|
|
#if WITH_HW_DISPLAY_DISPLAYPORT
|
|
if (timing_info->display_type == DISPLAY_TYPE_DP) {
|
|
displayport_quiesce();
|
|
result = 0;
|
|
}
|
|
#endif
|
|
|
|
#if WITH_HW_DISPLAY_HDMI
|
|
if (timing_info->display_type == DISPLAY_TYPE_HDMI) {
|
|
hdmi_quiesce();
|
|
result = 0;
|
|
}
|
|
#endif
|
|
|
|
display_set_enable(false, NULL);
|
|
|
|
if (main_window) {
|
|
free(main_window);
|
|
main_window = NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool display_get_enable(void)
|
|
{
|
|
bool display_enable;
|
|
|
|
#if WITH_DBE_CLCD
|
|
display_enable = clcd_get_enable_timing_generator();
|
|
#elif WITH_DBE_RGBOUT
|
|
display_enable = rgbout_get_enable_timing_generator();
|
|
#elif WITH_DBE_ADBE
|
|
display_enable = adbe_get_enable_timing_generator();
|
|
#else
|
|
# error "missing display_backend_enable_timing_generator"
|
|
#endif
|
|
display_enabled_valid = true;
|
|
|
|
return display_enable;
|
|
}
|
|
|
|
void display_set_enable(bool enable, u_int32_t *color)
|
|
{
|
|
if (!display_enabled_valid)
|
|
display_enabled = display_get_enable();
|
|
|
|
if (enable == display_enabled) return;
|
|
|
|
if (enable) {
|
|
if (color != NULL) {
|
|
display_default_color = *color;
|
|
}
|
|
|
|
fill_rect(&main_window->c, 0, 0, main_window->width, main_window->height, display_default_color);
|
|
|
|
display_backend_enable_timing_generator(true);
|
|
|
|
display_enabled = true;
|
|
|
|
} else {
|
|
|
|
display_backend_enable_timing_generator(false);
|
|
|
|
display_enabled = false;
|
|
}
|
|
}
|
|
|
|
bool display_set_rotation(bool rotate180)
|
|
{
|
|
#if WITH_DFE_ADFE_V2
|
|
adfe_set_axis_flip(0, rotate180, rotate180);
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void display_delay_frames(u_int32_t frames)
|
|
{
|
|
task_sleep(frames * display_frame_us);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//With iBoot inside display memory (1 plane Color)
|
|
// PANIC_BASE -------------------------------
|
|
// | iBoot |
|
|
// |-----------------------------|
|
|
// | (unused) |
|
|
// |-----------------------------|
|
|
// | db_virt (draw) |
|
|
// |-----------------------------|
|
|
// | fb |
|
|
// |-----------------------------|<---- region_valid
|
|
// | images |
|
|
// |-----------------------------|<---- scratch_base + 4K
|
|
// | flatten images |
|
|
// DISPLAY_BASE --> ------------------------------ <---- scratch_base
|
|
//With iBoot outside display memory (1 plane Color)
|
|
// PANIC_BASE -------------------------------
|
|
// | (unused) |
|
|
// |-----------------------------|
|
|
// | db_virt (draw) |
|
|
// |-----------------------------|
|
|
// | fb |
|
|
// |-----------------------------|<---- region_valid
|
|
// | images |
|
|
// |-----------------------------|<---- scratch_base + 4K
|
|
// | flatten images |
|
|
// DISPLAY_BASE --> ------------------------------ <---- scratch_base
|
|
//With iBoot outside display memory (2 plane Color)
|
|
// PANIC_BASE -------------------------------
|
|
// | (unused) |
|
|
// |-----------------------------|
|
|
// | plane 1 db_virt (draw) |
|
|
// |-----------------------------|
|
|
// | plane 0 db_virt (draw) |
|
|
// |-----------------------------|
|
|
// | plane 1 fb |
|
|
// |-----------------------------|
|
|
// | plane 0 fb |
|
|
// |-----------------------------|<---- region_valid
|
|
// | images |
|
|
// |-----------------------------|<---- scratch_base + 4K
|
|
// | flatten images |
|
|
// DISPLAY_BASE --> ------------------------------ <---- scratch_base
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
struct display_window *display_create_window(u_int32_t x, u_int32_t y, u_int32_t width, u_int32_t height, enum colorspace color)
|
|
{
|
|
struct display_window *w;
|
|
u_int32_t stridelen_in_pixels = width, depth;
|
|
uint32_t stridelen_in_bytes;
|
|
addr_t fb_base;
|
|
size_t fb_size;
|
|
size_t fb_region, region_valid;
|
|
uint32_t num_of_planes;
|
|
uint32_t plane1_depth;
|
|
|
|
switch (color) {
|
|
case CS_RGB565 :
|
|
depth = 16;
|
|
num_of_planes = 1;
|
|
break;
|
|
|
|
case CS_RGB888 :
|
|
depth = 32;
|
|
num_of_planes = 1;
|
|
break;
|
|
|
|
case CS_ARGB8101010 :
|
|
//the RGB plane is 32
|
|
depth = 32;
|
|
num_of_planes = 2;
|
|
//Alpha plane is 8
|
|
plane1_depth = 8;
|
|
break;
|
|
|
|
default :
|
|
return 0;
|
|
}
|
|
|
|
w = malloc(sizeof(struct display_window));
|
|
|
|
w->active = false;
|
|
w->cs = color;
|
|
w->depth = depth;
|
|
w->pos_x = x;
|
|
w->pos_y = y;
|
|
w->width = width;
|
|
w->height = height;
|
|
stridelen_in_bytes = (width * depth) / 8;
|
|
stridelen_in_bytes = (stridelen_in_bytes + LINEAR_STRIDE_ALIGNMENT_MASK) & ~LINEAR_STRIDE_ALIGNMENT_MASK;
|
|
w->stride = stridelen_in_bytes;
|
|
stridelen_in_pixels = stridelen_in_bytes / (depth/8);
|
|
|
|
// Calculate the frame buffer size rounded up to a page
|
|
fb_size = (w->height * w->stride + 0xFFF) & ~0xFFF;
|
|
|
|
//If we have more planes, more region_valid memory is required. Adjust the size accordingly
|
|
if (num_of_planes > 1) {
|
|
size_t plane1_stride_in_bytes = (width * (plane1_depth/8));
|
|
plane1_stride_in_bytes = (plane1_stride_in_bytes + LINEAR_STRIDE_ALIGNMENT_MASK) & ~LINEAR_STRIDE_ALIGNMENT_MASK;
|
|
size_t plane1_fb_size = (plane1_stride_in_bytes + 0xFFF) & ~0xFFF;
|
|
fb_size += plane1_fb_size;
|
|
}
|
|
|
|
|
|
//on 32 bit systems, the fb_base has to account for 1 fb + iBoot region. The iBoot region goes away for OS purposes yet the fb remains
|
|
region_valid = fb_region = (fb_size * TARGET_FB_MULT)/10;
|
|
#ifdef PROTECTED_REGION_SIZE
|
|
if(fb_region < fb_size * 2 + PROTECTED_REGION_SIZE) fb_region = fb_size * 2 + PROTECTED_REGION_SIZE ;
|
|
region_valid = fb_region - PROTECTED_REGION_SIZE;
|
|
#endif
|
|
// Calculate the frame buffer base assuming multiple buffers (as defined by TARGET_FB_MULT) from PANIC_BASE
|
|
// N.B We use PANIC_BASE since DISPLAY_BASE is used for image manipulation region
|
|
fb_base = PANIC_BASE - fb_region;
|
|
|
|
platform_init_display_mem(&fb_base, &fb_size);
|
|
#ifdef DISPLAY_BASE_NONALIGNED
|
|
set_canvas(&w->c, (void *)(PANIC_BASE - fb_region), region_valid, width, height, stridelen_in_pixels, color);
|
|
#else
|
|
set_canvas(&w->c, (void *)fb_base, region_valid, width, height, stridelen_in_pixels, color);
|
|
#endif
|
|
paint_init(w, DISPLAY_BASE, fb_base - DISPLAY_BASE);
|
|
|
|
adfe_set_ui_layer(0, color, get_plane(0), get_plane(1), width, height);
|
|
|
|
display_activate_window(w);
|
|
|
|
return w;
|
|
}
|
|
|
|
void display_set_background_color(u_int32_t color)
|
|
{
|
|
adfe_set_background_color(color);
|
|
}
|
|
|
|
void display_activate_window(struct display_window *win)
|
|
{
|
|
u_int32_t layer = 0;
|
|
|
|
if (layer > 1) return;
|
|
|
|
win->active = true;
|
|
|
|
adfe_activate_window();
|
|
}
|
|
|
|
static int display_set_timings(struct display_timing *timing)
|
|
{
|
|
int result = 0;
|
|
#if WITH_HW_DISPLAY_DISPLAYPORT
|
|
result = displayport_set_timings(timing);
|
|
#endif
|
|
#if WITH_HW_DISPLAY_HDMI
|
|
result = hdmi_set_timings(timing);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
|
|
static void display_backend_enable_clocks(bool enable)
|
|
{
|
|
#if WITH_DBE_CLCD
|
|
clcd_enable_clocks(enable);
|
|
#elif WITH_DBE_RGBOUT
|
|
rgbout_enable_clocks(enable);
|
|
#elif WITH_DBE_ADBE
|
|
adbe_enable_clocks(enable);
|
|
#else
|
|
# error "missing display_backend_enable_clocks"
|
|
#endif
|
|
}
|
|
|
|
static void display_backend_init(struct display_timing *timing)
|
|
{
|
|
#if WITH_DBE_CLCD
|
|
clcd_init(timing);
|
|
#elif WITH_DBE_RGBOUT
|
|
rgbout_init(timing);
|
|
#elif WITH_DBE_ADBE
|
|
adbe_init(timing);
|
|
#else
|
|
# error "missing display_backend_init"
|
|
#endif
|
|
}
|
|
|
|
static void display_backend_install_gamma_table(u_int32_t *red_lut, u_int32_t *green_lut, u_int32_t *blue_lut, struct syscfg_wpcl *wpcl)
|
|
{
|
|
#if WITH_DBE_CLCD
|
|
clcd_install_gamma_table(red_lut, green_lut, blue_lut, wpcl);
|
|
#elif WITH_DBE_RGBOUT
|
|
rgbout_install_gamma_table(red_lut, green_lut, blue_lut, wpcl);
|
|
#elif WITH_DBE_ADBE
|
|
adbe_install_gamma_table(red_lut, green_lut, blue_lut, wpcl);
|
|
#else
|
|
# error "missing display_backend_load_gamma_table"
|
|
#endif
|
|
}
|
|
|
|
static void display_backend_enable_timing_generator(bool enable)
|
|
{
|
|
#if WITH_DBE_CLCD
|
|
clcd_enable_timing_generator(enable);
|
|
#elif WITH_DBE_RGBOUT
|
|
rgbout_enable_timing_generator(enable);
|
|
#elif WITH_DBE_ADBE
|
|
adbe_enable_timing_generator(enable);
|
|
#else
|
|
# error "missing display_backend_enable_timing_generator"
|
|
#endif
|
|
}
|