/* $NetBSD: tifb.c,v 1.7 2017/03/01 16:27:25 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. */ /* * Copyright 2013 Oleksandr Tymoshenko * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 framebuffer driver for TI 35xx built-in video controller * tested on beaglebone */ #include __KERNEL_RCSID(0, "$NetBSD: tifb.c,v 1.7 2017/03/01 16:27:25 skrll Exp $"); #include "opt_omap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TI_AM335X # include # include # include # include #endif #include #include #include #include #include #include "locators.h" struct tifb_softc { device_t sc_dev; void *sc_ih; 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; size_t sc_palettesize; int sc_stride; void *sc_fbaddr, *sc_vramaddr; bus_addr_t sc_fbhwaddr; uint16_t *sc_palette; uint32_t sc_dispc_config; 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); struct tifb_panel_info *sc_pi; }; #define TIFB_READ(sc, reg) bus_space_read_4(sc->sc_iot, sc->sc_regh, reg) #define TIFB_WRITE(sc, reg, val) \ bus_space_write_4(sc->sc_iot, sc->sc_regh, reg, val) static int tifb_match(device_t, cfdata_t, void *); static void tifb_attach(device_t, device_t, void *); static int tifb_intr(void *); #ifdef TI_AM335X static void am335x_clk_lcdc_activate(void); static int am335x_clk_get_arm_disp_freq(unsigned int *); #endif CFATTACH_DECL_NEW(tifb, sizeof(struct tifb_softc), tifb_match, tifb_attach, NULL, NULL); static int tifb_ioctl(void *, void *, u_long, void *, int, struct lwp *); static paddr_t tifb_mmap(void *, void *, off_t, int); static void tifb_init_screen(void *, struct vcons_screen *, int, long *); static int tifb_putcmap(struct tifb_softc *, struct wsdisplay_cmap *); static int tifb_getcmap(struct tifb_softc *, struct wsdisplay_cmap *); static void tifb_restore_palette(struct tifb_softc *, int, int); #if 0 static int tifb_set_depth(struct tifb_softc *, int); #endif static void tifb_set_video(struct tifb_softc *, int); struct wsdisplay_accessops tifb_accessops = { tifb_ioctl, tifb_mmap, NULL, /* alloc_screen */ NULL, /* free_screen */ NULL, /* show_screen */ NULL, /* load_font */ NULL, /* pollc */ NULL /* scroll */ }; extern const u_char rasops_cmap[768]; static struct evcnt ev_sync_lost; static struct evcnt ev_palette; static struct evcnt ev_eof0; static struct evcnt ev_eof1; static struct evcnt ev_fifo_underflow; static struct evcnt ev_ac_bias; static struct evcnt ev_frame_done; static struct evcnt ev_others; static uint32_t am335x_lcd_calc_divisor(uint32_t reference, uint32_t freq) { uint32_t div; /* Raster mode case: divisors are in range from 2 to 255 */ for (div = 2; div < 255; div++) if (reference/div <= freq) return div; return 255; } static int tifb_match(device_t parent, cfdata_t match, void *aux) { struct obio_attach_args *obio = aux; if ((obio->obio_addr == OBIOCF_ADDR_DEFAULT) || (obio->obio_size == OBIOCF_SIZE_DEFAULT)) return 0; return 1; } static void tifb_attach(device_t parent, device_t self, void *aux) { struct tifb_softc *sc = device_private(self); struct obio_attach_args *obio = aux; struct rasops_info *ri; struct wsemuldisplaydev_attach_args aa; prop_dictionary_t dict = device_properties(self); prop_object_t panel_info; unsigned long defattr; bool is_console = false; uint32_t reg, timing0, timing1, timing2, burst_log; int segs, i, j, n, div, ref_freq; #ifdef TI_AM335X int ret; const char *mode; u_int state; struct tifb_padconf { const char *padname; const char *padmode; }; const struct tifb_padconf tifb_padconf_data[] = { {"LCD_DATA0", "lcd_data0"}, {"LCD_DATA1", "lcd_data1"}, {"LCD_DATA2", "lcd_data2"}, {"LCD_DATA3", "lcd_data3"}, {"LCD_DATA4", "lcd_data4"}, {"LCD_DATA5", "lcd_data5"}, {"LCD_DATA6", "lcd_data6"}, {"LCD_DATA7", "lcd_data7"}, {"LCD_DATA8", "lcd_data8"}, {"LCD_DATA9", "lcd_data9"}, {"LCD_DATA10", "lcd_data10"}, {"LCD_DATA11", "lcd_data11"}, {"LCD_DATA12", "lcd_data12"}, {"LCD_DATA13", "lcd_data13"}, {"LCD_DATA14", "lcd_data14"}, {"LCD_DATA15", "lcd_data15"}, {"GPMC_AD15", "lcd_data16"}, {"GPMC_AD14", "lcd_data17"}, {"GPMC_AD13", "lcd_data18"}, {"GPMC_AD12", "lcd_data19"}, {"GPMC_AD11", "lcd_data20"}, {"GPMC_AD10", "lcd_data21"}, {"GPMC_AD9", "lcd_data22"}, {"GPMC_AD8", "lcd_data23"}, }; const struct tifb_padconf tifb_padconf_ctrl[] = { {"LCD_VSYNC", "lcd_vsync"}, {"LCD_HSYNC", "lcd_hsync"}, {"LCD_PCLK", "lcd_pclk"}, {"LCD_AC_BIAS_EN", "lcd_ac_bias_en"}, }; #endif prop_dictionary_get_bool(dict, "is_console", &is_console); panel_info = prop_dictionary_get(dict, "panel-info"); if (panel_info == NULL) { aprint_error_dev(self, "no panel-info property\n"); return; } KASSERT(prop_object_type(panel_info) == PROP_TYPE_DATA); sc->sc_pi = __UNCONST(prop_data_data_nocopy(panel_info)); evcnt_attach_dynamic(&ev_sync_lost, EVCNT_TYPE_MISC, NULL, "lcd", "sync lost"); evcnt_attach_dynamic(&ev_palette, EVCNT_TYPE_MISC, NULL, "lcd", "palette loaded"); evcnt_attach_dynamic(&ev_eof0, EVCNT_TYPE_MISC, NULL, "lcd", "eof0"); evcnt_attach_dynamic(&ev_eof1, EVCNT_TYPE_MISC, NULL, "lcd", "eof1"); evcnt_attach_dynamic(&ev_fifo_underflow, EVCNT_TYPE_MISC, NULL, "lcd", "fifo underflow"); evcnt_attach_dynamic(&ev_ac_bias, EVCNT_TYPE_MISC, NULL, "lcd", "ac bias"); evcnt_attach_dynamic(&ev_frame_done, EVCNT_TYPE_MISC, NULL, "lcd", "frame_done"); evcnt_attach_dynamic(&ev_others, EVCNT_TYPE_MISC, NULL, "lcd", "others"); sc->sc_iot = obio->obio_iot; sc->sc_dev = self; sc->sc_dmat = obio->obio_dmat; if (bus_space_map(obio->obio_iot, obio->obio_addr, obio->obio_size, 0, &sc->sc_regh)) { aprint_error(": couldn't map register space\n"); return; } sc->sc_palettesize = 32; switch (sc->sc_pi->panel_bpp) { case 24: if (!sc->sc_pi->panel_tft) { aprint_error_dev(self, "bpp 24 only support tft, not attaching\n"); return; } break; case 12: case 16: if (sc->sc_pi->panel_mono) { aprint_error_dev(self, "bpp 12/16 only support color, not attaching\n"); return; } case 8: sc->sc_palettesize = 512; break; case 4: case 2: case 1: break; default: aprint_error_dev(self, "bogus display bpp, not attaching: bpp %d\n", sc->sc_pi->panel_bpp); return; } sc->sc_stride = sc->sc_pi->panel_width * sc->sc_pi->panel_bpp / 8; if (sc->sc_pi->panel_width == 0 || sc->sc_pi->panel_height == 0) { aprint_error_dev(self, "bogus display size, not attaching\n"); return; } if (obio->obio_intr == OBIOCF_INTR_DEFAULT) { aprint_error(": no interrupt\n"); bus_space_unmap(obio->obio_iot, sc->sc_regh, obio->obio_size); return; } sc->sc_ih = intr_establish(obio->obio_intr, IPL_NET, IST_LEVEL, tifb_intr, sc); KASSERT(sc->sc_ih != NULL); /* setup video DMA */ sc->sc_vramsize = sc->sc_palettesize + sc->sc_stride * sc->sc_pi->panel_height; if (bus_dmamem_alloc(sc->sc_dmat, sc->sc_vramsize, 0, 0, sc->sc_dmamem, 1, &segs, BUS_DMA_NOWAIT) != 0) { 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_PREFETCHABLE) != 0) { aprint_error_dev(sc->sc_dev, "failed to map video RAM\n"); return; } sc->sc_fbaddr = (uint8_t *)sc->sc_vramaddr + sc->sc_palettesize; sc->sc_palette = 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; } memset((void *)sc->sc_vramaddr, 0, sc->sc_vramsize); switch (sc->sc_pi->panel_bpp) { case 8: j = 0; for (i = 0; i < (1 << sc->sc_pi->panel_bpp); 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; } break; case 12: case 16: case 24: break; default: n = 1; switch (sc->sc_pi->panel_bpp) { case 1: n = 15; break; case 2: n = 5; break; } for (i = 0; i < (1 << sc->sc_pi->panel_bpp); i++) { sc->sc_cmap_red[i] = i * n; sc->sc_cmap_green[i] = i * n; sc->sc_cmap_blue[i] = i * n; } } tifb_restore_palette(sc, 0, 1 << sc->sc_pi->panel_bpp); #ifdef TI_AM335X /* configure output pins */ for (i = 0; i < ((sc->sc_pi->panel_bpp == 16) ? 16 : 24); i++) { if (sitara_cm_padconf_get(tifb_padconf_data[i].padname, &mode, &state) == 0) { aprint_debug(": %s mode %s state %d ", tifb_padconf_data[i].padname, mode, state); } if (strcmp(mode, tifb_padconf_data[i].padname) == 0) continue; if (sitara_cm_padconf_set(tifb_padconf_data[i].padname, tifb_padconf_data[i].padmode, 0x08) != 0) { aprint_error(": can't switch %s pad from %s to %s\n", tifb_padconf_data[i].padname, mode, tifb_padconf_data[i].padmode); return; } } for (i = 0; i < __arraycount(tifb_padconf_ctrl); i++) { if (sitara_cm_padconf_get(tifb_padconf_ctrl[i].padname, &mode, &state) == 0) { aprint_debug(": %s mode %s state %d ", tifb_padconf_ctrl[i].padname, mode, state); } if (strcmp(mode, tifb_padconf_ctrl[i].padmode) == 0) continue; if (sitara_cm_padconf_set(tifb_padconf_ctrl[i].padname, tifb_padconf_ctrl[i].padmode, 0) != 0) { aprint_error(": can't switch %s pad from %s to %s\n", tifb_padconf_ctrl[i].padname, mode, tifb_padconf_ctrl[i].padmode); return; } } am335x_clk_lcdc_activate(); /* get reference clk freq */ ret = am335x_clk_get_arm_disp_freq(&ref_freq); if (ret != 0) { aprint_error_dev(self, "display clock not enabled\n"); return; } #endif aprint_normal(": TI LCD controller\n"); aprint_debug_dev(self, ": fb@%p, palette@%p\n", sc->sc_fbaddr, sc->sc_palette); /* Only raster mode is supported */ reg = CTRL_RASTER_MODE; div = am335x_lcd_calc_divisor(ref_freq, sc->sc_pi->panel_pxl_clk); reg |= (div << CTRL_DIV_SHIFT); TIFB_WRITE(sc, LCD_CTRL, reg); aprint_debug_dev(self, ": ref_freq %d div %d\n", ref_freq, div); /* Set timing */ timing0 = RASTER_TIMING_0_HFP(sc->sc_pi->panel_hfp) | RASTER_TIMING_0_HBP(sc->sc_pi->panel_hbp) | RASTER_TIMING_0_HSW(sc->sc_pi->panel_hsw); timing1 = RASTER_TIMING_1_VFP(sc->sc_pi->panel_vfp) | RASTER_TIMING_1_VBP(sc->sc_pi->panel_vbp) | RASTER_TIMING_1_VSW(sc->sc_pi->panel_vsw); timing2 = RASTER_TIMING_2_HFP(sc->sc_pi->panel_hfp) | RASTER_TIMING_2_HBP(sc->sc_pi->panel_hbp) | RASTER_TIMING_2_HSW(sc->sc_pi->panel_hsw); /* Pixels per line */ timing0 |= RASTER_TIMING_0_PPL(sc->sc_pi->panel_width); /* Lines per panel */ timing1 |= RASTER_TIMING_1_LPP(sc->sc_pi->panel_height); timing2 |= RASTER_TIMING_2_LPP(sc->sc_pi->panel_height); /* clock signal settings */ if (sc->sc_pi->panel_sync_ctrl) timing2 |= RASTER_TIMING_2_PHSVS; if (sc->sc_pi->panel_sync_edge) timing2 |= RASTER_TIMING_2_PHSVS_RISE; else timing2 |= RASTER_TIMING_2_PHSVS_FALL; if (sc->sc_pi->panel_invert_hsync) timing2 |= RASTER_TIMING_2_IHS; if (sc->sc_pi->panel_invert_vsync) timing2 |= RASTER_TIMING_2_IVS; if (sc->sc_pi->panel_invert_pxl_clk) timing2 |= RASTER_TIMING_2_IPC; /* AC bias */ timing2 |= RASTER_TIMING_2_ACB(sc->sc_pi->panel_ac_bias); timing2 |= RASTER_TIMING_2_ACBI(sc->sc_pi->panel_ac_bias_intrpt); TIFB_WRITE(sc, LCD_RASTER_TIMING_0, timing0); TIFB_WRITE(sc, LCD_RASTER_TIMING_1, timing1); TIFB_WRITE(sc, LCD_RASTER_TIMING_2, timing2); aprint_debug_dev(self, ": timings 0x%x 0x%x 0x%x\n", timing0, timing1, timing2); /* DMA settings */ reg = 0; /* Find power of 2 for current burst size */ switch (sc->sc_pi->panel_dma_burst_sz) { case 1: burst_log = 0; break; case 2: burst_log = 1; break; case 4: burst_log = 2; break; case 8: burst_log = 3; break; case 16: default: burst_log = 4; break; } reg |= (burst_log << LCDDMA_CTRL_BURST_SIZE_SHIFT); /* XXX: FIFO TH */ reg |= (0 << LCDDMA_CTRL_TH_FIFO_RDY_SHIFT); reg |= LCDDMA_CTRL_FB0_ONLY; TIFB_WRITE(sc, LCD_LCDDMA_CTRL, reg); aprint_debug_dev(self, ": LCD_LCDDMA_CTRL 0x%x\n", reg); TIFB_WRITE(sc, LCD_LCDDMA_FB0_BASE, sc->sc_dmamem->ds_addr); TIFB_WRITE(sc, LCD_LCDDMA_FB0_CEILING, sc->sc_dmamem->ds_addr + sc->sc_vramsize - 1); aprint_debug_dev(self, ": LCD_LCDDMA 0x%x 0x%x\n", (u_int)sc->sc_dmamem->ds_addr, (u_int)(sc->sc_dmamem->ds_addr + sc->sc_vramsize - 1)); reg = 0; if (sc->sc_pi->panel_tft) { reg |= RASTER_CTRL_LCDTFT; switch (sc->sc_pi->panel_bpp) { case 24: reg |= RASTER_CTRL_TFT24; break; case 8: case 4: case 2: case 1: if (sc->sc_pi->panel_tft_alt_mode) reg |= RASTER_CTRL_TFTMAP; break; } } else { if (sc->sc_pi->panel_mono) { reg |= RASTER_CTRL_LCDBW; if (sc->sc_pi->panel_bpp == 8) reg |= RASTER_CTRL_MONO8B; } else if (sc->sc_pi->panel_bpp == 16) reg |= RASTER_CTRL_STN565; } reg |= RASTER_CTRL_REQDLY(sc->sc_pi->panel_fdd); switch (sc->sc_pi->panel_bpp) { case 12: case 16: case 24: reg |= RASTER_CTRL_PALMODE_DATA_ONLY; break; default: reg |= RASTER_CTRL_PALMODE_PALETTE_AND_DATA; if (sc->sc_pi->panel_rdorder) reg |= RASTER_CTRL_RDORDER; break; } TIFB_WRITE(sc, LCD_RASTER_CTRL, reg); aprint_debug_dev(self, ": LCD_RASTER_CTRL 0x%x\n", reg); TIFB_WRITE(sc, LCD_CLKC_ENABLE, CLKC_ENABLE_DMA | CLKC_ENABLE_CORE); TIFB_WRITE(sc, LCD_CLKC_RESET, CLKC_RESET_MAIN); DELAY(100); TIFB_WRITE(sc, LCD_CLKC_RESET, 0); aprint_debug_dev(self, ": LCD_CLKC_ENABLE 0x%x\n", TIFB_READ(sc, LCD_CLKC_ENABLE)); reg = IRQ_FUF | IRQ_PL | IRQ_ACB | IRQ_SYNC_LOST; TIFB_WRITE(sc, LCD_IRQENABLE_SET, reg); reg = TIFB_READ(sc, LCD_RASTER_CTRL); reg |= RASTER_CTRL_LCDEN; TIFB_WRITE(sc, LCD_RASTER_CTRL, reg); aprint_debug_dev(self, ": LCD_RASTER_CTRL 0x%x\n", TIFB_READ(sc, LCD_RASTER_CTRL)); TIFB_WRITE(sc, LCD_SYSCONFIG, SYSCONFIG_STANDBY_SMART | SYSCONFIG_IDLE_SMART); aprint_debug_dev(self, ": LCD_SYSCONFIG 0x%x\n", TIFB_READ(sc, LCD_SYSCONFIG)); /* attach wscons */ 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; vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, &tifb_accessops); sc->vd.init_screen = tifb_init_screen; /* init engine here */ 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; 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 = &tifb_accessops; aa.accesscookie = &sc->vd; config_found(sc->sc_dev, &aa, wsemuldisplaydevprint); } static int tifb_intr(void *v) { struct tifb_softc *sc = v; uint32_t reg; reg = TIFB_READ(sc, LCD_IRQSTATUS); TIFB_WRITE(sc, LCD_IRQSTATUS, reg); if (reg & IRQ_SYNC_LOST) { ev_sync_lost.ev_count++; reg = TIFB_READ(sc, LCD_RASTER_CTRL); reg &= ~RASTER_CTRL_LCDEN; TIFB_WRITE(sc, LCD_RASTER_CTRL, reg); reg = TIFB_READ(sc, LCD_RASTER_CTRL); reg |= RASTER_CTRL_LCDEN; TIFB_WRITE(sc, LCD_RASTER_CTRL, reg); return 0; } if (reg & IRQ_PL) { ev_palette.ev_count++; reg = TIFB_READ(sc, LCD_RASTER_CTRL); reg &= ~RASTER_CTRL_LCDEN; TIFB_WRITE(sc, LCD_RASTER_CTRL, reg); reg = TIFB_READ(sc, LCD_RASTER_CTRL); reg |= RASTER_CTRL_LCDEN; TIFB_WRITE(sc, LCD_RASTER_CTRL, reg); return 0; } if (reg & IRQ_FRAME_DONE) { ev_frame_done.ev_count++; reg &= ~IRQ_FRAME_DONE; } if (reg & IRQ_EOF0) { ev_eof0.ev_count++; TIFB_WRITE(sc, LCD_LCDDMA_FB0_BASE, sc->sc_dmamem->ds_addr); TIFB_WRITE(sc, LCD_LCDDMA_FB0_CEILING, sc->sc_dmamem->ds_addr + sc->sc_vramsize - 1); reg &= ~IRQ_EOF0; } if (reg & IRQ_EOF1) { ev_eof1.ev_count++; TIFB_WRITE(sc, LCD_LCDDMA_FB1_BASE, sc->sc_dmamem->ds_addr); TIFB_WRITE(sc, LCD_LCDDMA_FB1_CEILING, sc->sc_dmamem->ds_addr + sc->sc_vramsize - 1); reg &= ~IRQ_EOF1; } if (reg & IRQ_FUF) { ev_fifo_underflow.ev_count++; /* TODO: Handle FUF */ reg =~ IRQ_FUF; } if (reg & IRQ_ACB) { ev_ac_bias.ev_count++; /* TODO: Handle ACB */ reg =~ IRQ_ACB; } if (reg) { ev_others.ev_count++; printf("%s: unhandled irq status 0x%x\n", device_xname(sc->sc_dev), reg); } return 0; } static int tifb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) { struct vcons_data *vd = v; struct tifb_softc *sc = vd->cookie; struct wsdisplay_fbinfo *wdf; struct wsdisplayio_fbinfo *fbi; struct wsdisplay_curpos *cp; // struct wsdisplay_cursor *cursor; struct vcons_screen *ms = vd->active; int new_mode, *on, ret; switch (cmd) { case WSDISPLAYIO_GTYPE: *(u_int *)data = WSDISPLAY_TYPE_GENFB; return 0; case WSDISPLAYIO_GET_BUSID: ((struct wsdisplayio_bus_id *)data)->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 = ms->scr_ri.ri_depth; wdf->cmsize = 256; return 0; case WSDISPLAYIO_GETCMAP: return tifb_getcmap(sc, (struct wsdisplay_cmap *)data); case WSDISPLAYIO_PUTCMAP: return tifb_putcmap(sc, (struct wsdisplay_cmap *)data); case WSDISPLAYIO_LINEBYTES: *(u_int *)data = sc->sc_stride; return 0; case WSDISPLAYIO_SMODE: new_mode = *(int *)data; if (new_mode != sc->sc_mode) { sc->sc_mode = new_mode; #if 0 if (new_mode == WSDISPLAYIO_MODE_EMUL) { tifb_set_depth(sc, 16); vcons_redraw_screen(ms); } else tifb_set_depth(sc, 32); #endif } return 0; case WSDISPLAYIO_GET_FBINFO: fbi = data; ret = wsdisplayio_get_fbinfo(&ms->scr_ri, fbi); fbi->fbi_flags |= WSFB_VRAM_IS_RAM; fbi->fbi_fboffset = sc->sc_palettesize; return ret; case WSDISPLAYIO_GVIDEO: on = data; *on = 1; /* XXX sc->sc_video_is_on; */ return 0; case WSDISPLAYIO_SVIDEO: on = data; tifb_set_video(sc, *on); return 0; case WSDISPLAYIO_GCURPOS: cp = data; // cp->x = sc->sc_cursor_x; // cp->y = sc->sc_cursor_y; return 0; case WSDISPLAYIO_SCURPOS: cp = data; // omapfb_move_cursor(sc, cp->x, cp->y); return 0; case WSDISPLAYIO_GCURMAX: cp = data; cp->x = 64; cp->y = 64; return 0; case WSDISPLAYIO_SCURSOR: // cursor = data; // return omapfb_do_cursor(sc, cursor); break; } return EPASSTHROUGH; } static paddr_t tifb_mmap(void *v, void *vs, off_t offset, int prot) { paddr_t pa = -1; struct vcons_data *vd = v; struct tifb_softc *sc = vd->cookie; /* 'regular' framebuffer mmap()ing */ if (offset < sc->sc_stride * sc->sc_pi->panel_height) { pa = bus_dmamem_mmap(sc->sc_dmat, sc->sc_dmamem, 1, offset, prot, BUS_DMA_PREFETCHABLE); return pa; } return pa; } static void tifb_init_screen(void *cookie, struct vcons_screen *scr, int existing, long *defattr) { struct tifb_softc *sc = cookie; struct rasops_info *ri = &scr->scr_ri; ri->ri_depth = sc->sc_pi->panel_bpp; ri->ri_width = sc->sc_pi->panel_width; ri->ri_height = sc->sc_pi->panel_height; ri->ri_stride = sc->sc_stride; ri->ri_flg = RI_CENTER | RI_FULLCLEAR; ri->ri_bits = (char *)sc->sc_fbaddr; ri->ri_hwbits = NULL; if (existing) ri->ri_flg |= RI_CLEAR; rasops_init(ri, sc->sc_pi->panel_height / 8, sc->sc_pi->panel_width / 8); ri->ri_caps = WSSCREEN_WSCOLORS; rasops_reconfig(ri, sc->sc_pi->panel_height / ri->ri_font->fontheight, sc->sc_pi->panel_width / ri->ri_font->fontwidth); ri->ri_hw = scr; } static int tifb_putcmap(struct tifb_softc *sc, struct wsdisplay_cmap *cm) { return EINVAL; /* XXX */ #if 0 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++) { tifb_putpalreg(sc, index, *r, *g, *b); index++; r++, g++, b++; } return 0; #endif } static int tifb_getcmap(struct tifb_softc *sc, struct wsdisplay_cmap *cm) { return EINVAL; /* XXX */ #if 0 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; #endif } static void tifb_restore_palette(struct tifb_softc *sc, int index, int count) { uint32_t bpp; int r, g, b, i; memset(&sc->sc_palette[index], 0, sizeof(*sc->sc_palette) * count); switch (sc->sc_pi->panel_bpp) { case 1: bpp = PALETTE_BPP_1; break; case 2: bpp = PALETTE_BPP_2; break; case 4: bpp = PALETTE_BPP_4; break; case 8: bpp = PALETTE_BPP_8; break; default: bpp = PALETTE_BPP_XX; count = 0; break; } for (i = index; i < count; i++) { r = sc->sc_cmap_red[i]; g = sc->sc_cmap_green[i]; b = sc->sc_cmap_blue[i]; sc->sc_palette[i] = PALETTE_COLOR(r, g, b); } if (index == 0) sc->sc_palette[0] |= bpp; } #if 0 static int tifb_set_depth(struct tifb_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_panel->bpp = d; sc->sc_stride = sc->sc_panel->panel_width * (sc->sc_panel->bpp >> 3); /* clear the screen here */ memset(sc->sc_fbaddr, 0, sc->sc_stride * sc->sc_panel->panel_height); return 0; } #endif static void tifb_set_video(struct tifb_softc *sc, int on) { #if 0 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; #endif } #ifdef TI_AM335X static void am335x_clk_lcdc_activate(void) { /* Bypass mode */ prcm_write_4(AM335X_PRCM_CM_WKUP, CM_WKUP_CM_CLKMODE_DPLL_DISP, AM335X_PRCM_CM_CLKMODE_DPLL_MN_BYP_MODE); /* Make sure it's in bypass mode */ while (!(prcm_read_4(AM335X_PRCM_CM_WKUP, CM_WKUP_CM_IDLEST_DPLL_DISP) & (1 << 8))) DELAY(10); /* * For now set frequency to 5xSYSFREQ * More flexible control might be required */ prcm_write_4(AM335X_PRCM_CM_WKUP, CM_WKUP_CM_CLKSEL_DPLL_DISP, (5 << 8) | 0); /* Locked mode */ prcm_write_4(AM335X_PRCM_CM_WKUP, CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x7); int timeout = 10000; while ((!(prcm_read_4(AM335X_PRCM_CM_WKUP, CM_WKUP_CM_IDLEST_DPLL_DISP) & (1 << 0))) && timeout--) DELAY(10); /*set MODULEMODE to ENABLE(2) */ prcm_write_4(AM335X_PRCM_CM_PER, CM_PER_LCDC_CLKCTRL, 2); /* wait for MODULEMODE to become ENABLE(2) */ while ((prcm_read_4(AM335X_PRCM_CM_PER, CM_PER_LCDC_CLKCTRL) & 0x3) != 2) DELAY(10); /* wait for IDLEST to become Func(0) */ while(prcm_read_4(AM335X_PRCM_CM_PER, CM_PER_LCDC_CLKCTRL) & (3<<16)) DELAY(10); } static int am335x_clk_get_arm_disp_freq(unsigned int *freq) { uint32_t reg; #define DPLL_BYP_CLKSEL(reg) ((reg>>23) & 1) #define DPLL_DIV(reg) ((reg & 0x7f)+1) #define DPLL_MULT(reg) ((reg>>8) & 0x7FF) reg = prcm_read_4(AM335X_PRCM_CM_WKUP, CM_WKUP_CM_CLKSEL_DPLL_DISP); /*Check if we are running in bypass */ if (DPLL_BYP_CLKSEL(reg)) return ENXIO; *freq = DPLL_MULT(reg) * (omap_sys_clk / DPLL_DIV(reg)); return 0; } #endif