/* * Copyright (c) 2013 Jan Vesely * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup amdm37x_dispc * @{ */ /** * @file */ #include #include #include #include #include #include #include "amdm37x_dispc.h" #ifndef CONFIG_BFB_BPP #define CONFIG_BFB_BPP 24 #endif #ifndef CONFIG_BFB_WIDTH #define CONFIG_BFB_WIDTH 1024 #endif #ifndef CONFIG_BFB_HEIGHT #define CONFIG_BFB_HEIGHT 768 #endif static errno_t change_mode(visualizer_t *vis, vslmode_t mode); static errno_t handle_damage(visualizer_t *vs, sysarg_t x0, sysarg_t y0, sysarg_t width, sysarg_t height, sysarg_t x_offset, sysarg_t y_offset); static errno_t dummy(visualizer_t *vs) { return EOK; } static const visualizer_ops_t amdm37x_dispc_vis_ops = { .change_mode = change_mode, .handle_damage = handle_damage, .claim = dummy, .yield = dummy, .suspend = dummy, .wakeup = dummy, }; static const struct { unsigned bpp; pixel2visual_t func; } pixel2visual_table[] = { [VISUAL_INDIRECT_8] = { .bpp = 1, .func = pixel2bgr_323 }, [VISUAL_RGB_5_5_5_LE] = { .bpp = 2, .func = pixel2rgb_555_le }, [VISUAL_RGB_5_5_5_BE] = { .bpp = 2, .func = pixel2rgb_555_be }, [VISUAL_RGB_5_6_5_LE] = { .bpp = 2, .func = pixel2rgb_565_le }, [VISUAL_RGB_5_6_5_BE] = { .bpp = 2, .func = pixel2rgb_565_be }, [VISUAL_BGR_8_8_8] = { .bpp = 3, .func = pixel2bgr_888 }, [VISUAL_RGB_8_8_8] = { .bpp = 3, .func = pixel2rgb_888 }, [VISUAL_BGR_0_8_8_8] = { .bpp = 4, .func = pixel2rgb_0888 }, [VISUAL_BGR_8_8_8_0] = { .bpp = 4, .func = pixel2bgr_8880 }, [VISUAL_ABGR_8_8_8_8] = { .bpp = 4, .func = pixel2abgr_8888 }, [VISUAL_BGRA_8_8_8_8] = { .bpp = 4, .func = pixel2bgra_8888 }, [VISUAL_RGB_0_8_8_8] = { .bpp = 4, .func = pixel2rgb_0888 }, [VISUAL_RGB_8_8_8_0] = { .bpp = 4, .func = pixel2rgb_8880 }, [VISUAL_ARGB_8_8_8_8] = { .bpp = 4, .func = pixel2argb_8888 }, [VISUAL_RGBA_8_8_8_8] = { .bpp = 4, .func = pixel2rgba_8888 }, }; static void mode_init(vslmode_list_element_t *mode, unsigned width, unsigned height, visual_t visual) { mode->mode.index = 0; mode->mode.version = 0; mode->mode.refresh_rate = 0; mode->mode.screen_aspect.width = width; mode->mode.screen_aspect.height = height; mode->mode.screen_width = width; mode->mode.screen_height = height; mode->mode.cell_aspect.width = 1; mode->mode.cell_aspect.height = 1; mode->mode.cell_visual.pixel_visual = visual; link_initialize(&mode->link); } errno_t amdm37x_dispc_init(amdm37x_dispc_t *instance, visualizer_t *vis) { assert(instance); assert(vis); instance->fb_data = NULL; instance->size = 0; /* Default is 24bpp, use config option if available */ visual_t visual = VISUAL_BGR_8_8_8; switch (CONFIG_BFB_BPP) { case 8: visual = VISUAL_INDIRECT_8; break; case 16: visual = VISUAL_RGB_5_6_5_LE; break; case 24: visual = VISUAL_BGR_8_8_8; break; case 32: visual = VISUAL_RGB_8_8_8_0; break; default: return EINVAL; } errno_t ret = pio_enable((void *)AMDM37x_DISPC_BASE_ADDRESS, AMDM37x_DISPC_SIZE, (void **)&instance->regs); if (ret != EOK) { return EIO; } mode_init(&instance->modes[0], CONFIG_BFB_WIDTH, CONFIG_BFB_HEIGHT, visual); /* Handle vis stuff */ vis->dev_ctx = instance; vis->def_mode_idx = 0; vis->ops = amdm37x_dispc_vis_ops; list_append(&instance->modes[0].link, &vis->modes); return EOK; } errno_t amdm37x_dispc_fini(amdm37x_dispc_t *instance) { return EOK; } static errno_t amdm37x_dispc_setup_fb(amdm37x_dispc_regs_t *regs, unsigned x, unsigned y, unsigned bpp, uint32_t pa) { assert(regs); /* * Init sequence for dispc is in chapter 7.6.5.1.4 p. 1810, * no idea what parts of that work. */ /* Disable all interrupts */ regs->irqenable = 0; /* Pixel format specifics*/ uint32_t attrib_pixel_format = 0; uint32_t control_data_lanes = 0; switch (bpp) { case 32: attrib_pixel_format = AMDM37X_DISPC_GFX_ATTRIBUTES_FORMAT_RGBX; control_data_lanes = AMDM37X_DISPC_CONTROL_TFTDATALINES_24B; break; case 24: attrib_pixel_format = AMDM37X_DISPC_GFX_ATTRIBUTES_FORMAT_RGB24; control_data_lanes = AMDM37X_DISPC_CONTROL_TFTDATALINES_24B; break; case 16: attrib_pixel_format = AMDM37X_DISPC_GFX_ATTRIBUTES_FORMAT_RGB16; control_data_lanes = AMDM37X_DISPC_CONTROL_TFTDATALINES_16B; break; default: return EINVAL; } /* Prepare sizes */ const uint32_t size_reg = (((x - 1) & AMDM37X_DISPC_SIZE_WIDTH_MASK) << AMDM37X_DISPC_SIZE_WIDTH_SHIFT) | (((y - 1) & AMDM37X_DISPC_SIZE_HEIGHT_MASK) << AMDM37X_DISPC_SIZE_HEIGHT_SHIFT); /* modes taken from u-boot, for 1024x768 */ // TODO replace magic values with actual correct values #if 0 regs->timing_h = 0x1a4024c9; regs->timing_v = 0x02c00509; regs->pol_freq = 0x00007028; regs->divisor = 0x00010001; #endif /* setup output */ regs->size_lcd = size_reg; regs->size_dig = size_reg; /* Nice blue default color */ regs->default_color[0] = 0x0000ff; regs->default_color[1] = 0x0000ff; /* Setup control register */ uint32_t control = 0 | AMDM37X_DISPC_CONTROL_PCKFREEENABLE_FLAG | (control_data_lanes << AMDM37X_DISPC_CONTROL_TFTDATALINES_SHIFT) | AMDM37X_DISPC_CONTROL_GPOUT0_FLAG | AMDM37X_DISPC_CONTROL_GPOUT1_FLAG; regs->control = control; /* No gamma stuff only data */ uint32_t config = (AMDM37X_DISPC_CONFIG_LOADMODE_DATAEVERYFRAME << AMDM37X_DISPC_CONFIG_LOADMODE_SHIFT); regs->config = config; /* Set framebuffer base address */ regs->gfx.ba[0] = pa; regs->gfx.ba[1] = pa; regs->gfx.position = 0; /* Setup fb size */ regs->gfx.size = size_reg; /* Set pixel format */ uint32_t attribs = 0 | (attrib_pixel_format << AMDM37X_DISPC_GFX_ATTRIBUTES_FORMAT_SHIFT); regs->gfx.attributes = attribs; /* 0x03ff03c0 is the default */ regs->gfx.fifo_threshold = 0x03ff03c0; /* * This value should be stride - width, 1 means next pixel i.e. * stride == width */ regs->gfx.row_inc = 1; /* number of bytes to next pixel in BPP multiples */ regs->gfx.pixel_inc = 1; /* only used if video is played over fb */ regs->gfx.window_skip = 0; /* Gamma and palette table */ regs->gfx.table_ba = 0; /* enable frame buffer graphics */ regs->gfx.attributes |= AMDM37X_DISPC_GFX_ATTRIBUTES_ENABLE_FLAG; /* Update register values */ regs->control |= AMDM37X_DISPC_CONTROL_GOLCD_FLAG; regs->control |= AMDM37X_DISPC_CONTROL_GODIGITAL_FLAG; /* Enable output */ regs->control |= AMDM37X_DISPC_CONTROL_LCD_ENABLE_FLAG; regs->control |= AMDM37X_DISPC_CONTROL_DIGITAL_ENABLE_FLAG; return EOK; } static errno_t change_mode(visualizer_t *vis, vslmode_t mode) { assert(vis); assert(vis->dev_ctx); amdm37x_dispc_t *dispc = vis->dev_ctx; const visual_t visual = mode.cell_visual.pixel_visual; assert((size_t)visual < sizeof(pixel2visual_table) / sizeof(pixel2visual_table[0])); const unsigned bpp = pixel2visual_table[visual].bpp; pixel2visual_t p2v = pixel2visual_table[visual].func; const unsigned x = mode.screen_width; const unsigned y = mode.screen_height; ddf_log_note("Setting mode: %ux%ux%u\n", x, y, bpp * 8); const size_t size = ALIGN_UP(x * y * bpp, PAGE_SIZE); uintptr_t pa; void *buffer = AS_AREA_ANY; errno_t ret = dmamem_map_anonymous(size, DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &pa, &buffer); if (ret != EOK) { ddf_log_error("Failed to get new FB\n"); return ret; } if (dispc->fb_data) dmamem_unmap_anonymous(dispc->fb_data); dispc->fb_data = buffer; amdm37x_dispc_setup_fb(dispc->regs, x, y, bpp * 8, (uint32_t)pa); dispc->active_fb.idx = mode.index; dispc->active_fb.width = x; dispc->active_fb.height = y; dispc->active_fb.pitch = 0; dispc->active_fb.bpp = bpp; dispc->active_fb.pixel2visual = p2v; dispc->size = size; assert(mode.index < 1); return EOK; } static errno_t handle_damage(visualizer_t *vs, sysarg_t x0, sysarg_t y0, sysarg_t width, sysarg_t height, sysarg_t x_offset, sysarg_t y_offset) { assert(vs); assert(vs->dev_ctx); amdm37x_dispc_t *dispc = vs->dev_ctx; pixelmap_t *map = &vs->cells; #define FB_POS(x, y) \ (((y) * (dispc->active_fb.width + dispc->active_fb.pitch) + (x)) \ * dispc->active_fb.bpp) if (x_offset == 0 && y_offset == 0) { /* Faster damage routine ignoring offsets. */ for (sysarg_t y = y0; y < height + y0; ++y) { pixel_t *pixel = pixelmap_pixel_at(map, x0, y); for (sysarg_t x = x0; x < width + x0; ++x) { dispc->active_fb.pixel2visual( dispc->fb_data + FB_POS(x, y), *pixel++); } } } else { for (sysarg_t y = y0; y < height + y0; ++y) { for (sysarg_t x = x0; x < width + x0; ++x) { dispc->active_fb.pixel2visual( dispc->fb_data + FB_POS(x, y), *pixelmap_pixel_at(map, (x + x_offset) % map->width, (y + y_offset) % map->height)); } } } return EOK; } /** * @} */