/* $NetBSD: omap3_dss.c,v 1.7 2022/09/27 06:36:43 skrll Exp $ */ /* * Copyright (c) 2010 Michael Lorenz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * 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. */ /* * A console driver for OMAP 3530's built-in video controller * tested on beagleboard only so far */ #include "opt_wsdisplay_compat.h" #include __KERNEL_RCSID(0, "$NetBSD: omap3_dss.c,v 1.7 2022/09/27 06:36:43 skrll Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct omapfb_softc { device_t sc_dev; bus_space_tag_t sc_iot; bus_dma_tag_t sc_dmat; bus_space_handle_t sc_regh; bus_dmamap_t sc_dmamap; bus_dma_segment_t sc_dmamem[1]; size_t sc_vramsize; int sc_width, sc_height, sc_depth, sc_stride; int sc_locked; void *sc_fbaddr, *sc_vramaddr; int sc_cursor_offset; uint32_t *sc_cursor_img; int sc_cursor_x, sc_cursor_y; int sc_hot_x, sc_hot_y; uint8_t sc_cursor_bitmap[8 * 64]; uint8_t sc_cursor_mask[8 * 64]; uint32_t sc_cursor_cmap[4]; bus_addr_t sc_fbhwaddr; uint32_t *sc_clut; uint32_t sc_dispc_config; int sc_video_is_on; struct vcons_screen sc_console_screen; struct wsscreen_descr sc_defaultscreen_descr; const struct wsscreen_descr *sc_screens[1]; struct wsscreen_list sc_screenlist; struct vcons_data vd; int sc_mode; uint8_t sc_cmap_red[256], sc_cmap_green[256], sc_cmap_blue[256]; void (*sc_putchar)(void *, int, int, u_int, long); uint8_t sc_edid_data[1024]; size_t sc_edid_size; }; static int omapfb_match(device_t, cfdata_t, void *); static void omapfb_attach(device_t, device_t, void *); CFATTACH_DECL_NEW(omap3_dss, sizeof(struct omapfb_softc), omapfb_match, omapfb_attach, NULL, NULL); static int omapfb_ioctl(void *, void *, u_long, void *, int, struct lwp *); static paddr_t omapfb_mmap(void *, void *, off_t, int); static void omapfb_init_screen(void *, struct vcons_screen *, int, long *); static int omapfb_putcmap(struct omapfb_softc *, struct wsdisplay_cmap *); static int omapfb_getcmap(struct omapfb_softc *, struct wsdisplay_cmap *); static void omapfb_restore_palette(struct omapfb_softc *); static void omapfb_putpalreg(struct omapfb_softc *, int, uint8_t, uint8_t, uint8_t); static int omapfb_set_depth(struct omapfb_softc *, int); static void omapfb_set_video(struct omapfb_softc *, int); static void omapfb_move_cursor(struct omapfb_softc *, int, int); static int omapfb_do_cursor(struct omapfb_softc *, struct wsdisplay_cursor *); #if NOMAPDMA > 0 static void omapfb_init(struct omapfb_softc *); static void omapfb_wait_idle(struct omapfb_softc *); static void omapfb_rectfill(struct omapfb_softc *, int, int, int, int, uint32_t); static void omapfb_bitblt(struct omapfb_softc *, int, int, int, int, int, int, int); static void omapfb_cursor(void *, int, int, int); static void omapfb_putchar(void *, int, int, u_int, long); static void omapfb_copycols(void *, int, int, int, int); static void omapfb_erasecols(void *, int, int, int, long); static void omapfb_copyrows(void *, int, int, int); static void omapfb_eraserows(void *, int, int, long); #endif /* NOMAPDMA > 0 */ struct wsdisplay_accessops omapfb_accessops = { omapfb_ioctl, omapfb_mmap, NULL, /* alloc_screen */ NULL, /* free_screen */ NULL, /* show_screen */ NULL, /* load_font */ NULL, /* pollc */ NULL /* scroll */ }; uint32_t venc_mode_ntsc[] = { 0x00000000, 0x00000001, 0x00008040, 0x00000359, 0x0000020c, 0x00000000, 0x043f2631, 0x00000000, 0x00000102, 0x0000016c, 0x0000012f, 0x00000043, 0x00000038, 0x00000007, 0x00000001, 0x00000038, 0x21f07c1f, 0x00000000, 0x01310011, 0x0000f003, 0x00000000, 0x069300f4, 0x0016020c, 0x00060107, 0x008e0350, 0x000f0359, 0x01a00000, 0x020701a0, 0x01ac0024, 0x020d01ac, 0x00000006, 0x03480078, 0x02060024, 0x0001008a, 0x01ac0106, 0x01060006, 0x00140001, 0x00010001, 0x00f90000, 0x0000000d, 0x00000000}; extern const u_char rasops_cmap[768]; static const struct device_compatible_entry compat_data[] = { { .compat = "ti,omap3-dss" }, DEVICE_COMPAT_EOL }; static int omapfb_console_phandle = -1; static int omapfb_match(device_t parent, cfdata_t match, void *aux) { struct fdt_attach_args * const faa = aux; return of_compatible_match(faa->faa_phandle, compat_data); } static void omapfb_attach(device_t parent, device_t self, void *aux) { struct omapfb_softc *sc = device_private(self); struct fdt_attach_args *faa = aux; const int phandle = faa->faa_phandle; struct rasops_info *ri; struct wsemuldisplaydev_attach_args aa; prop_dictionary_t dict; prop_data_t edid_data; unsigned long defattr; #ifdef WSDISPLAY_MULTICONS bool is_console = true; #else bool is_console = phandle == omapfb_console_phandle; #endif uint32_t sz, reg; int segs, i, j; bus_addr_t addr; bus_size_t size; if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { aprint_error(": couldn't get registers\n"); return; } sc->sc_dev = self; sc->sc_iot = faa->faa_bst; sc->sc_dmat = faa->faa_dmat; if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_regh) != 0) { aprint_error(": couldn't map registers\n"); return; } aprint_naive("\n"); aprint_normal(": OMAP onboard video\n"); sc->sc_video_is_on = 1; /* * XXX * different u-boot versions initialize the graphics controller in * different ways, so we look for the display resolution in a few * different places... */ sz = bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_SIZE); if (sz == 0) { sz = bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_SIZE_LCD); } if (sz == 0) { sz = bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_SIZE_DIG); } /* ... and make sure it ends up where we need it */ bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_SIZE, sz); sc->sc_width = (sz & 0xfff) + 1; sc->sc_height = ((sz & 0x0fff0000 ) >> 16) + 1; sc->sc_depth = 16; sc->sc_stride = sc->sc_width << 1; if (sc->sc_width == 1 || sc->sc_height == 1) { aprint_error_dev(self, "bogus display size, not attaching\n"); return; } printf("%s: firmware set up %d x %d\n", device_xname(self), sc->sc_width, sc->sc_height); #if 0 printf("DSS revision: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DSS_REVISION)); #endif dict = device_properties(self); edid_data = prop_dictionary_get(dict, "EDID"); if (edid_data != NULL) { struct edid_info ei; sc->sc_edid_size = uimin(prop_data_size(edid_data), 1024); memset(sc->sc_edid_data, 0, sizeof(sc->sc_edid_data)); memcpy(sc->sc_edid_data, prop_data_value(edid_data), sc->sc_edid_size); edid_parse(sc->sc_edid_data, &ei); edid_print(&ei); } /* setup video DMA */ sc->sc_vramsize = (12 << 20) + PAGE_SIZE; /* 12MB + CLUT */ if (bus_dmamem_alloc(sc->sc_dmat, sc->sc_vramsize, 0, 0, sc->sc_dmamem, 1, &segs, BUS_DMA_NOWAIT) != 0) { panic("boo!\n"); aprint_error_dev(sc->sc_dev, "failed to allocate video memory\n"); return; } if (bus_dmamem_map(sc->sc_dmat, sc->sc_dmamem, 1, sc->sc_vramsize, &sc->sc_vramaddr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT) != 0) { aprint_error_dev(sc->sc_dev, "failed to map video RAM\n"); return; } sc->sc_fbaddr = (uint8_t *)sc->sc_vramaddr + PAGE_SIZE; sc->sc_clut = sc->sc_vramaddr; if (bus_dmamap_create(sc->sc_dmat, sc->sc_vramsize, 1, sc->sc_vramsize, 0, BUS_DMA_NOWAIT, &sc->sc_dmamap) != 0) { aprint_error_dev(sc->sc_dev, "failed to create DMA map\n"); return; } if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_vramaddr, sc->sc_vramsize, NULL, BUS_DMA_NOWAIT) != 0) { aprint_error_dev(sc->sc_dev, "failed to load DMA map\n"); return; } if (sc->sc_depth == 8) { j = 0; for (i = 0; i < 256; i++) { sc->sc_cmap_red[i] = rasops_cmap[j]; sc->sc_cmap_green[i] = rasops_cmap[j + 1]; sc->sc_cmap_blue[i] = rasops_cmap[j + 2]; j += 3; } } else { for (i = 0; i < 256; i++) { sc->sc_cmap_red[i] = i; sc->sc_cmap_green[i] = i; sc->sc_cmap_blue[i] = i; } } omapfb_restore_palette(sc); /* now that we have video memory, stick it to the video controller */ reg = bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_SYSCONFIG); reg &= ~(OMAP_DISPC_SYSC_STANDBY_MASK | OMAP_DISPC_SYSC_IDLE_MASK); reg |= OMAP_DISPC_SYSC_SMART_STANDBY | OMAP_DISPC_SYSC_SMART_IDLE | OMAP_DISPC_SYSC_WAKEUP_ENABLE | OMAP_SYSCONF_AUTOIDLE; bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_SYSCONFIG, reg); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DSS_SYSCONFIG, OMAP_SYSCONF_AUTOIDLE); reg = OMAP_DISPC_CFG_TV_ALPHA_EN | OMAP_DISPC_CFG_LCD_ALPHA_EN; bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONFIG, reg); sc->sc_dispc_config = reg; /* we use overlay 1 for the console and X */ bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GLOBAL_ALPHA, 0x00ff00ff); sc->sc_fbhwaddr = sc->sc_dmamem->ds_addr + PAGE_SIZE; bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID1_BASE_0, sc->sc_fbhwaddr); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID1_POSITION, 0); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID1_SIZE, ((sc->sc_height - 1) << 16) | (sc->sc_width - 1)); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID1_PICTURE_SIZE, ((sc->sc_height - 1) << 16) | (sc->sc_width - 1)); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID1_ROW_INC, 1); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID1_PIXEL_INC, 1); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID1_PRELOAD, 0x60); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID1_ATTRIBUTES, OMAP_VID_ATTR_ENABLE | OMAP_VID_ATTR_BURST_16x32 | OMAP_VID_ATTR_RGB16 | OMAP_VID_ATTR_REPLICATION); /* turn off overlay 2 */ bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID2_ATTRIBUTES, 0); /* initialize the gfx layer for use as hardware cursor */ sc->sc_cursor_cmap[0] = 0; sc->sc_cursor_offset = (12 << 20) - (64 * 64 * 4); sc->sc_cursor_img = (uint32_t *)((uint8_t *)sc->sc_fbaddr + sc->sc_cursor_offset); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_BASE_0, sc->sc_fbhwaddr + sc->sc_cursor_offset); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_TABLE_BASE, sc->sc_dmamem->ds_addr); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_SIZE, 0x003f003f); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_POSITION, 0x00100010); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_PRELOAD, 0x60); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_ATTRIBUTES, /*OMAP_DISPC_ATTR_ENABLE |*/ OMAP_DISPC_ATTR_BURST_16x32 | OMAP_DISPC_ATTR_ARGB32 | OMAP_DISPC_ATTR_REPLICATION); #if 0 printf("dss_control: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DSS_CONTROL)); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DSS_CONTROL, /*OMAP_DSSCTRL_DISPC_CLK_SWITCH |*/ OMAP_DSSCTRL_CLOCK_MODE | OMAP_DSSCTRL_VENC_CLOCK_4X | OMAP_DSSCTRL_DAC_DEMEN); #endif #if 0 /* VENC to NTSC mode */ int adr = OMAPFB_VENC_F_CONTROL; for (i = 0; i < __arraycount(venc_mode_ntsc); i++) { bus_space_write_4(sc->sc_iot, sc->sc_regh, adr, venc_mode_ntsc[i]); adr += 4; } bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_VENC_F_CONTROL, venc_mode_ntsc[0]); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_VENC_SYNC_CTRL, venc_mode_ntsc[2]); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_DEFAULT_COLOR_1, 0x00ff0000); #endif /* now we make sure the video output is actually running */ reg = bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL, reg | OMAP_DISPC_CTRL_GO_LCD | OMAP_DISPC_CTRL_GO_DIGITAL); #ifdef OMAPFB_DEBUG printf("attr: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_ATTRIBUTES)); printf("preload: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_PRELOAD)); printf("config: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONFIG)); printf("control: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL)); printf("dss_control: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DSS_CONTROL)); printf("threshold: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_FIFO_THRESH)); printf("GFX size: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_SIZE)); printf("row inc: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_ROW_INC)); printf("pixel inc: %08x\n", bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_PIXEL_INC)); #endif sc->sc_defaultscreen_descr = (struct wsscreen_descr){ "default", 0, 0, NULL, 8, 16, WSSCREEN_WSCOLORS | WSSCREEN_HILIT, NULL }; sc->sc_screens[0] = &sc->sc_defaultscreen_descr; sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens}; sc->sc_mode = WSDISPLAYIO_MODE_EMUL; sc->sc_locked = 0; vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, &omapfb_accessops); sc->vd.init_screen = omapfb_init_screen; /* init engine here */ #if NOMAPDMA > 0 omapfb_init(sc); #endif ri = &sc->sc_console_screen.scr_ri; vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, &defattr); sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; #if NOMAPDMA > 0 omapfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height, ri->ri_devcmap[(defattr >> 16) & 0xff]); #endif sc->sc_defaultscreen_descr.textops = &ri->ri_ops; sc->sc_defaultscreen_descr.capabilities = ri->ri_caps; sc->sc_defaultscreen_descr.nrows = ri->ri_rows; sc->sc_defaultscreen_descr.ncols = ri->ri_cols; if (is_console) wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, defattr); vcons_replay_msgbuf(&sc->sc_console_screen); aa.console = is_console; aa.scrdata = &sc->sc_screenlist; aa.accessops = &omapfb_accessops; aa.accesscookie = &sc->vd; config_found(sc->sc_dev, &aa, wsemuldisplaydevprint, CFARGS_NONE); #ifdef OMAPFB_DEBUG #if NOMAPDMA > 0 omapfb_rectfill(sc, 100, 100, 100, 100, 0xe000); omapfb_rectfill(sc, 100, 200, 100, 100, 0x01f8); omapfb_rectfill(sc, 200, 100, 100, 100, 0x01f8); omapfb_rectfill(sc, 200, 200, 100, 100, 0xe000); omapfb_bitblt(sc, 100, 100, 400, 100, 200, 200, 0); /* let's see if we can draw something */ printf("OMAPDMAC_CDAC: %08x\n", omapdma_read_ch_reg(0, OMAPDMAC_CDAC)); printf("OMAPDMAC_CSR: %08x\n", omapdma_read_ch_reg(0, OMAPDMAC_CSR)); printf("OMAPDMAC_CCR: %08x\n", omapdma_read_ch_reg(0, OMAPDMAC_CCR)); #endif #endif } static int omapfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) { struct vcons_data *vd = v; struct omapfb_softc *sc = vd->cookie; struct wsdisplay_fbinfo *wdf; struct vcons_screen *ms = vd->active; switch (cmd) { case WSDISPLAYIO_GTYPE: *(u_int *)data = WSDISPLAY_TYPE_OMAP3; return 0; case WSDISPLAYIO_GET_BUSID: { struct wsdisplayio_bus_id *busid; busid = data; busid->bus_type = WSDISPLAYIO_BUS_SOC; return 0; } case WSDISPLAYIO_GINFO: if (ms == NULL) return ENODEV; wdf = (void *)data; wdf->height = ms->scr_ri.ri_height; wdf->width = ms->scr_ri.ri_width; wdf->depth = 32; wdf->cmsize = 256; return 0; case WSDISPLAYIO_GETCMAP: return omapfb_getcmap(sc, (struct wsdisplay_cmap *)data); case WSDISPLAYIO_PUTCMAP: return omapfb_putcmap(sc, (struct wsdisplay_cmap *)data); case WSDISPLAYIO_LINEBYTES: *(u_int *)data = sc->sc_width * 4; return 0; case WSDISPLAYIO_SMODE: { int new_mode = *(int*)data; if (new_mode != sc->sc_mode) { sc->sc_mode = new_mode; if (new_mode == WSDISPLAYIO_MODE_EMUL) { omapfb_set_depth(sc, 16); vcons_redraw_screen(ms); } else { omapfb_set_depth(sc, 32); } } } return 0; case WSDISPLAYIO_GET_FBINFO: { struct wsdisplayio_fbinfo *fbi = data; fbi->fbi_width = sc->sc_width; fbi->fbi_height = sc->sc_height; fbi->fbi_stride = sc->sc_width << 2; fbi->fbi_bitsperpixel = 32; fbi->fbi_pixeltype = WSFB_RGB; fbi->fbi_subtype.fbi_rgbmasks.red_offset = 16; fbi->fbi_subtype.fbi_rgbmasks.red_size = 8; fbi->fbi_subtype.fbi_rgbmasks.green_offset = 8; fbi->fbi_subtype.fbi_rgbmasks.green_size = 8; fbi->fbi_subtype.fbi_rgbmasks.blue_offset = 0; fbi->fbi_subtype.fbi_rgbmasks.blue_size = 8; fbi->fbi_subtype.fbi_rgbmasks.alpha_offset = 0; fbi->fbi_subtype.fbi_rgbmasks.alpha_size = 0; fbi->fbi_flags = 0; fbi->fbi_fbsize = sc->sc_vramsize; fbi->fbi_fboffset = 0; fbi->fbi_flags = WSFB_VRAM_IS_RAM; } return 0; case WSDISPLAYIO_GVIDEO: { int *on = data; *on = sc->sc_video_is_on; } return 0; case WSDISPLAYIO_SVIDEO: { int *on = data; omapfb_set_video(sc, *on); } return 0; case WSDISPLAYIO_GCURPOS: { struct wsdisplay_curpos *cp = (void *)data; cp->x = sc->sc_cursor_x; cp->y = sc->sc_cursor_y; } return 0; case WSDISPLAYIO_SCURPOS: { struct wsdisplay_curpos *cp = (void *)data; omapfb_move_cursor(sc, cp->x, cp->y); } return 0; case WSDISPLAYIO_GCURMAX: { struct wsdisplay_curpos *cp = (void *)data; cp->x = 64; cp->y = 64; } return 0; case WSDISPLAYIO_SCURSOR: { struct wsdisplay_cursor *cursor = (void *)data; return omapfb_do_cursor(sc, cursor); } } return EPASSTHROUGH; } static paddr_t omapfb_mmap(void *v, void *vs, off_t offset, int prot) { paddr_t pa = -1; struct vcons_data *vd = v; struct omapfb_softc *sc = vd->cookie; /* 'regular' framebuffer mmap()ing */ if (offset < sc->sc_vramsize) { pa = bus_dmamem_mmap(sc->sc_dmat, sc->sc_dmamem, 1, offset + PAGE_SIZE, prot, BUS_DMA_PREFETCHABLE); return pa; } return pa; } static void omapfb_init_screen(void *cookie, struct vcons_screen *scr, int existing, long *defattr) { struct omapfb_softc *sc = cookie; struct rasops_info *ri = &scr->scr_ri; ri->ri_depth = sc->sc_depth; ri->ri_width = sc->sc_width; ri->ri_height = sc->sc_height; ri->ri_stride = sc->sc_stride; ri->ri_flg = RI_CENTER | RI_FULLCLEAR; ri->ri_bits = (char *)sc->sc_fbaddr; #if NOMAPDMA < 1 scr->scr_flags |= VCONS_DONT_READ; #endif if (existing) { ri->ri_flg |= RI_CLEAR; } rasops_init(ri, sc->sc_height / 8, sc->sc_width / 8); ri->ri_caps = WSSCREEN_WSCOLORS; rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, sc->sc_width / ri->ri_font->fontwidth); ri->ri_hw = scr; #if NOMAPDMA > 0 ri->ri_ops.copyrows = omapfb_copyrows; ri->ri_ops.copycols = omapfb_copycols; ri->ri_ops.eraserows = omapfb_eraserows; ri->ri_ops.erasecols = omapfb_erasecols; ri->ri_ops.cursor = omapfb_cursor; sc->sc_putchar = ri->ri_ops.putchar; ri->ri_ops.putchar = omapfb_putchar; #endif } static int omapfb_putcmap(struct omapfb_softc *sc, struct wsdisplay_cmap *cm) { u_char *r, *g, *b; u_int index = cm->index; u_int count = cm->count; int i, error; u_char rbuf[256], gbuf[256], bbuf[256]; if (cm->index >= 256 || cm->count > 256 || (cm->index + cm->count) > 256) return EINVAL; error = copyin(cm->red, &rbuf[index], count); if (error) return error; error = copyin(cm->green, &gbuf[index], count); if (error) return error; error = copyin(cm->blue, &bbuf[index], count); if (error) return error; memcpy(&sc->sc_cmap_red[index], &rbuf[index], count); memcpy(&sc->sc_cmap_green[index], &gbuf[index], count); memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count); r = &sc->sc_cmap_red[index]; g = &sc->sc_cmap_green[index]; b = &sc->sc_cmap_blue[index]; for (i = 0; i < count; i++) { omapfb_putpalreg(sc, index, *r, *g, *b); index++; r++, g++, b++; } return 0; } static int omapfb_getcmap(struct omapfb_softc *sc, struct wsdisplay_cmap *cm) { u_int index = cm->index; u_int count = cm->count; int error; if (index >= 255 || count > 256 || index + count > 256) return EINVAL; error = copyout(&sc->sc_cmap_red[index], cm->red, count); if (error) return error; error = copyout(&sc->sc_cmap_green[index], cm->green, count); if (error) return error; error = copyout(&sc->sc_cmap_blue[index], cm->blue, count); if (error) return error; return 0; } static void omapfb_restore_palette(struct omapfb_softc *sc) { int i; for (i = 0; i < 256; i++) { omapfb_putpalreg(sc, i, sc->sc_cmap_red[i], sc->sc_cmap_green[i], sc->sc_cmap_blue[i]); } } static void omapfb_putpalreg(struct omapfb_softc *sc, int idx, uint8_t r, uint8_t g, uint8_t b) { uint32_t reg; if ((idx < 0) || (idx > 255)) return; /* whack the DAC */ reg = (r << 16) | (g << 8) | b; sc->sc_clut[idx] = reg; } static int omapfb_set_depth(struct omapfb_softc *sc, int d) { uint32_t reg; reg = OMAP_VID_ATTR_ENABLE | OMAP_VID_ATTR_BURST_16x32 | OMAP_VID_ATTR_REPLICATION; switch (d) { case 16: reg |= OMAP_VID_ATTR_RGB16; break; case 32: reg |= OMAP_VID_ATTR_RGB24; break; default: aprint_error_dev(sc->sc_dev, "unsupported depth (%d)\n", d); return EINVAL; } bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_VID1_ATTRIBUTES, reg); /* * now tell the video controller that we're done mucking around and * actually update its settings */ reg = bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL, reg | OMAP_DISPC_CTRL_GO_LCD | OMAP_DISPC_CTRL_GO_DIGITAL); sc->sc_depth = d; sc->sc_stride = sc->sc_width * (sc->sc_depth >> 3); /* clear the screen here */ #if NOMAPDMA > 0 omapfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height, 0); #else memset(sc->sc_fbaddr, 0, sc->sc_stride * sc->sc_height); #endif return 0; } static void omapfb_set_video(struct omapfb_softc *sc, int on) { uint32_t reg; if (on == sc->sc_video_is_on) return; if (on) { bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONFIG, sc->sc_dispc_config); on = 1; } else { bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONFIG, sc->sc_dispc_config | OMAP_DISPC_CFG_VSYNC_GATED | OMAP_DISPC_CFG_HSYNC_GATED | OMAP_DISPC_CFG_PIXELCLK_GATED | OMAP_DISPC_CFG_PIXELDATA_GATED); } /* * now tell the video controller that we're done mucking around and * actually update its settings */ reg = bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL, reg | OMAP_DISPC_CTRL_GO_LCD | OMAP_DISPC_CTRL_GO_DIGITAL); aprint_debug_dev(sc->sc_dev, "%s %08x\n", __func__, bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONFIG)); sc->sc_video_is_on = on; } #if NOMAPDMA > 0 static void omapfb_init(struct omapfb_softc *sc) { omapdma_write_ch_reg(0, OMAPDMAC_CLNK_CTRL, 0); omapdma_write_ch_reg(0, OMAPDMAC_CICRI, 0); omapdma_write_ch_reg(0, OMAPDMAC_CSDPI, CSDPI_SRC_BURST_64 | CSDPI_DST_BURST_64 | CSDPI_WRITE_POSTED | CSDPI_DATA_TYPE_16); } static void omapfb_wait_idle(struct omapfb_softc *sc) { while ((omapdma_read_ch_reg(0, OMAPDMAC_CCR) & CCR_WR_ACTIVE) != 0); } static void omapfb_rectfill(struct omapfb_softc *sc, int x, int y, int wi, int he, uint32_t colour) { int bpp = sc->sc_depth >> 3; /* bytes per pixel */ int width_in_bytes = wi * bpp; uint32_t daddr; daddr = sc->sc_fbhwaddr + sc->sc_stride * y + x * bpp; omapfb_wait_idle(sc); /* * stupid hardware * in 32bit mode the DMA controller always writes 0 into the upper * byte, so we can use this mode only if we actually want that */ if (((colour & 0xff00) == 0) && (((daddr | width_in_bytes) & 3) == 0)) { /* * everything is properly aligned so we can copy stuff in * 32bit chunks instead of pixel by pixel */ wi = wi >> 1; /* just in case */ colour |= colour << 16; omapdma_write_ch_reg(0, OMAPDMAC_CSDPI, CSDPI_SRC_BURST_64 | CSDPI_DST_BURST_64 | CSDPI_DST_PACKED | CSDPI_WRITE_POSTED_EXCEPT_LAST | CSDPI_DATA_TYPE_32); } else { omapdma_write_ch_reg(0, OMAPDMAC_CSDPI, CSDPI_SRC_BURST_64 | CSDPI_DST_BURST_64 | CSDPI_DST_PACKED | CSDPI_WRITE_POSTED_EXCEPT_LAST | CSDPI_DATA_TYPE_16); } omapdma_write_ch_reg(0, OMAPDMAC_CEN, wi); omapdma_write_ch_reg(0, OMAPDMAC_CFN, he); omapdma_write_ch_reg(0, OMAPDMAC_CDSA, daddr); omapdma_write_ch_reg(0, OMAPDMAC_CCR, CCR_CONST_FILL_ENABLE | CCR_DST_AMODE_DOUBLE_INDEX | CCR_SRC_AMODE_CONST_ADDR); omapdma_write_ch_reg(0, OMAPDMAC_CDEI, 1); omapdma_write_ch_reg(0, OMAPDMAC_CDFI, (sc->sc_stride - width_in_bytes) + 1); omapdma_write_ch_reg(0, OMAPDMAC_COLOR, colour); omapdma_write_ch_reg(0, OMAPDMAC_CCR, CCR_CONST_FILL_ENABLE | CCR_DST_AMODE_DOUBLE_INDEX | CCR_SRC_AMODE_CONST_ADDR | CCR_ENABLE); } static void omapfb_bitblt(struct omapfb_softc *sc, int xs, int ys, int xd, int yd, int wi, int he, int rop) { int bpp = sc->sc_depth >> 3; /* bytes per pixel */ int width_in_bytes = wi * bpp; int hstep, vstep; uint32_t saddr, daddr; saddr = sc->sc_fbhwaddr + sc->sc_stride * ys + xs * bpp; daddr = sc->sc_fbhwaddr + sc->sc_stride * yd + xd * bpp; if (ys < yd) { /* need to go vertically backwards */ vstep = 1 - (sc->sc_stride + width_in_bytes); saddr += sc->sc_stride * (he - 1); daddr += sc->sc_stride * (he - 1); } else vstep = (sc->sc_stride - width_in_bytes) + 1; if ((xs < xd) && (ys == yd)) { /* * need to go horizontally backwards, only needed if source * and destination pixels are on the same line */ hstep = 1 - (sc->sc_depth >> 2); vstep = sc->sc_stride + bpp * (wi - 1) + 1; saddr += bpp * (wi - 1); daddr += bpp * (wi - 1); } else hstep = 1; omapfb_wait_idle(sc); if (((saddr | daddr | width_in_bytes) & 3) == 0) { /* * everything is properly aligned so we can copy stuff in * 32bit chunks instead of pixel by pixel */ wi = wi >> 1; omapdma_write_ch_reg(0, OMAPDMAC_CSDPI, CSDPI_SRC_BURST_64 | CSDPI_DST_BURST_64 | CSDPI_DST_PACKED | CSDPI_WRITE_POSTED_EXCEPT_LAST | CSDPI_DATA_TYPE_32); } else { omapdma_write_ch_reg(0, OMAPDMAC_CSDPI, CSDPI_SRC_BURST_64 | CSDPI_DST_BURST_64 | CSDPI_DST_PACKED | CSDPI_WRITE_POSTED_EXCEPT_LAST | CSDPI_DATA_TYPE_16); } omapdma_write_ch_reg(0, OMAPDMAC_CEN, wi); omapdma_write_ch_reg(0, OMAPDMAC_CFN, he); omapdma_write_ch_reg(0, OMAPDMAC_CSSA, saddr); omapdma_write_ch_reg(0, OMAPDMAC_CDSA, daddr); omapdma_write_ch_reg(0, OMAPDMAC_CCR, CCR_DST_AMODE_DOUBLE_INDEX | CCR_SRC_AMODE_DOUBLE_INDEX); omapdma_write_ch_reg(0, OMAPDMAC_CSEI, hstep); omapdma_write_ch_reg(0, OMAPDMAC_CSFI, vstep); omapdma_write_ch_reg(0, OMAPDMAC_CDEI, hstep); omapdma_write_ch_reg(0, OMAPDMAC_CDFI, vstep); omapdma_write_ch_reg(0, OMAPDMAC_CCR, CCR_DST_AMODE_DOUBLE_INDEX | CCR_SRC_AMODE_DOUBLE_INDEX | CCR_ENABLE); } static void omapfb_cursor(void *cookie, int on, int row, int col) { struct rasops_info *ri = cookie; struct vcons_screen *scr = ri->ri_hw; struct omapfb_softc *sc = scr->scr_cookie; int pos; pos = col + row * ri->ri_cols; pos += vcons_offset_to_zero(scr); if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) { if (ri->ri_flg & RI_CURSOR) { omapfb_putchar(cookie, row, col, scr->scr_chars[pos], scr->scr_attrs[pos]); ri->ri_flg &= ~RI_CURSOR; } ri->ri_crow = row; ri->ri_ccol = col; if (on) { omapfb_putchar(cookie, row, col, scr->scr_chars[pos], scr->scr_attrs[pos] ^ 0x0f0f0000); ri->ri_flg |= RI_CURSOR; } } else { scr->scr_ri.ri_crow = row; scr->scr_ri.ri_ccol = col; scr->scr_ri.ri_flg &= ~RI_CURSOR; } } static void omapfb_putchar(void *cookie, int row, int col, u_int c, long attr) { struct rasops_info *ri = cookie; struct vcons_screen *scr = ri->ri_hw; struct omapfb_softc *sc = scr->scr_cookie; if (c == 0x20) { uint32_t fg, bg, ul; rasops_unpack_attr(attr, &fg, &bg, &ul); omapfb_rectfill(sc, ri->ri_xorigin + ri->ri_font->fontwidth * col, ri->ri_yorigin + ri->ri_font->fontheight * row, ri->ri_font->fontwidth, ri->ri_font->fontheight, ri->ri_devcmap[bg]); return; } omapfb_wait_idle(sc); sc->sc_putchar(cookie, row, col, c, attr); } static void omapfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) { struct rasops_info *ri = cookie; struct vcons_screen *scr = ri->ri_hw; struct omapfb_softc *sc = scr->scr_cookie; int32_t xs, xd, y, width, height; if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) { xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol; xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol; y = ri->ri_yorigin + ri->ri_font->fontheight * row; width = ri->ri_font->fontwidth * ncols; height = ri->ri_font->fontheight; omapfb_bitblt(sc, xs, y, xd, y, width, height, 12); } } static void omapfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) { struct rasops_info *ri = cookie; struct vcons_screen *scr = ri->ri_hw; struct omapfb_softc *sc = scr->scr_cookie; int32_t x, y, width, height, fg, bg, ul; if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) { x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol; y = ri->ri_yorigin + ri->ri_font->fontheight * row; width = ri->ri_font->fontwidth * ncols; height = ri->ri_font->fontheight; rasops_unpack_attr(fillattr, &fg, &bg, &ul); omapfb_rectfill(sc, x, y, width, height, ri->ri_devcmap[bg]); } } static void omapfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows) { struct rasops_info *ri = cookie; struct vcons_screen *scr = ri->ri_hw; struct omapfb_softc *sc = scr->scr_cookie; int32_t x, ys, yd, width, height; if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) { x = ri->ri_xorigin; ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow; yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow; width = ri->ri_emuwidth; height = ri->ri_font->fontheight * nrows; omapfb_bitblt(sc, x, ys, x, yd, width, height, 12); } } static void omapfb_eraserows(void *cookie, int row, int nrows, long fillattr) { struct rasops_info *ri = cookie; struct vcons_screen *scr = ri->ri_hw; struct omapfb_softc *sc = scr->scr_cookie; int32_t x, y, width, height, fg, bg, ul; if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) { x = ri->ri_xorigin; y = ri->ri_yorigin + ri->ri_font->fontheight * row; width = ri->ri_emuwidth; height = ri->ri_font->fontheight * nrows; rasops_unpack_attr(fillattr, &fg, &bg, &ul); omapfb_rectfill(sc, x, y, width, height, ri->ri_devcmap[bg]); } } #endif /* NOMAPDMA > 0 */ static void omapfb_move_cursor(struct omapfb_softc *sc, int x, int y) { uint32_t pos, reg, addr; int xx, yy, wi = 64, he = 64; /* * we need to special case anything and everything where the cursor * may be partially off screen */ addr = sc->sc_fbhwaddr + sc->sc_cursor_offset; xx = x - sc->sc_hot_x; yy = y - sc->sc_hot_y; /* * if we're off to the top or left we need to shif the start address * and shrink the gfx layer size */ if (xx < 0) { wi += xx; addr -= xx * 4; xx = 0; } if (yy < 0) { he += yy; addr -= yy * 64 * 4; yy = 0; } if (xx > (sc->sc_width - 64)) { wi -= (xx + 64 - sc->sc_width); } if (yy > (sc->sc_height - 64)) { he -= (yy + 64 - sc->sc_height); } pos = (yy << 16) | xx; bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_BASE_0, addr); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_POSITION, pos); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_SIZE, ((he - 1) << 16) | (wi - 1)); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_ROW_INC, (64 - wi) * 4 + 1); /* * now tell the video controller that we're done mucking around and * actually update its settings */ reg = bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL, reg | OMAP_DISPC_CTRL_GO_LCD | OMAP_DISPC_CTRL_GO_DIGITAL); } static int omapfb_do_cursor(struct omapfb_softc * sc, struct wsdisplay_cursor *cur) { int whack = 0; int shape = 0; if (cur->which & WSDISPLAY_CURSOR_DOCUR) { uint32_t attr = OMAP_DISPC_ATTR_BURST_16x32 | OMAP_DISPC_ATTR_ARGB32 | OMAP_DISPC_ATTR_REPLICATION; if (cur->enable) attr |= OMAP_DISPC_ATTR_ENABLE; bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_GFX_ATTRIBUTES, attr); whack = 1; } if (cur->which & WSDISPLAY_CURSOR_DOHOT) { sc->sc_hot_x = cur->hot.x; sc->sc_hot_y = cur->hot.y; cur->which |= WSDISPLAY_CURSOR_DOPOS; } if (cur->which & WSDISPLAY_CURSOR_DOPOS) { omapfb_move_cursor(sc, cur->pos.x, cur->pos.y); } if (cur->which & WSDISPLAY_CURSOR_DOCMAP) { int i; uint32_t val; for (i = 0; i < uimin(cur->cmap.count, 3); i++) { val = (cur->cmap.red[i] << 16 ) | (cur->cmap.green[i] << 8) | (cur->cmap.blue[i] ) | 0xff000000; sc->sc_cursor_cmap[i + cur->cmap.index + 2] = val; } shape = 1; } if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) { copyin(cur->mask, sc->sc_cursor_mask, 64 * 8); copyin(cur->image, sc->sc_cursor_bitmap, 64 * 8); shape = 1; } if (shape) { int i, j, idx; uint8_t mask; for (i = 0; i < 64 * 8; i++) { mask = 0x01; for (j = 0; j < 8; j++) { idx = ((sc->sc_cursor_mask[i] & mask) ? 2 : 0) | ((sc->sc_cursor_bitmap[i] & mask) ? 1 : 0); sc->sc_cursor_img[i * 8 + j] = sc->sc_cursor_cmap[idx]; mask = mask << 1; } } } if (whack) { uint32_t reg; reg = bus_space_read_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL); bus_space_write_4(sc->sc_iot, sc->sc_regh, OMAPFB_DISPC_CONTROL, reg | OMAP_DISPC_CTRL_GO_LCD | OMAP_DISPC_CTRL_GO_DIGITAL); } return 0; } static int omapfb_console_match(int phandle) { return of_compatible_match(phandle, compat_data); } static void omapfb_console_consinit(struct fdt_attach_args *faa, u_int uart_freq) { omapfb_console_phandle = faa->faa_phandle; } static const struct fdt_console omapfb_fdt_console = { .match = omapfb_console_match, .consinit = omapfb_console_consinit, }; FDT_CONSOLE(omapfb, &omapfb_fdt_console);