1259728Sray/*- 2259728Sray * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 3259728Sray * Copyright (c) 2012, 2013 The FreeBSD Foundation 4259728Sray * All rights reserved. 5259728Sray * 6259728Sray * Portions of this software were developed by Oleksandr Rybalko 7259728Sray * under sponsorship from the FreeBSD Foundation. 8259728Sray * 9259728Sray * Redistribution and use in source and binary forms, with or without 10259728Sray * modification, are permitted provided that the following conditions 11259728Sray * are met: 12259728Sray * 1. Redistributions of source code must retain the above copyright 13259728Sray * notice, this list of conditions and the following disclaimer. 14259728Sray * 2. Redistributions in binary form must reproduce the above copyright 15259728Sray * notice, this list of conditions and the following disclaimer in the 16259728Sray * documentation and/or other materials provided with the distribution. 17259728Sray * 18259728Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19259728Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20259728Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21259728Sray * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22259728Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23259728Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24259728Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25259728Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26259728Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27259728Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28259728Sray * SUCH DAMAGE. 29259728Sray * 30259728Sray */ 31259728Sray#include <sys/cdefs.h> 32259728Sray__FBSDID("$FreeBSD$"); 33259728Sray 34259728Sray#include <sys/param.h> 35259728Sray#include <sys/systm.h> 36259728Sray#include <sys/bio.h> 37259728Sray#include <sys/bus.h> 38259728Sray#include <sys/conf.h> 39259728Sray#include <sys/endian.h> 40259728Sray#include <sys/kernel.h> 41259728Sray#include <sys/kthread.h> 42259728Sray#include <sys/lock.h> 43259728Sray#include <sys/malloc.h> 44259728Sray#include <sys/module.h> 45259728Sray#include <sys/mutex.h> 46259728Sray#include <sys/queue.h> 47259728Sray#include <sys/resource.h> 48259728Sray#include <sys/rman.h> 49259728Sray#include <sys/time.h> 50259728Sray#include <sys/timetc.h> 51259728Sray#include <sys/fbio.h> 52259728Sray#include <sys/consio.h> 53259728Sray#include <sys/eventhandler.h> 54259728Sray 55259728Sray#include <sys/kdb.h> 56259728Sray 57259728Sray#include <machine/bus.h> 58259728Sray#include <machine/cpu.h> 59259728Sray#include <machine/cpufunc.h> 60266088Sian#include <machine/fdt.h> 61259728Sray#include <machine/resource.h> 62259728Sray#include <machine/frame.h> 63259728Sray#include <machine/intr.h> 64259728Sray 65259728Sray#include <dev/fdt/fdt_common.h> 66259728Sray#include <dev/ofw/ofw_bus.h> 67259728Sray#include <dev/ofw/ofw_bus_subr.h> 68259728Sray 69259728Sray#include <dev/vt/vt.h> 70259728Sray#include <dev/vt/colors/vt_termcolors.h> 71259728Sray 72259728Sray#include <arm/freescale/imx/imx51_ccmvar.h> 73259728Sray 74259728Sray#include <arm/freescale/imx/imx51_ipuv3reg.h> 75259728Sray 76259728Sray#include "fb_if.h" 77259728Sray 78259728Sray#define IMX51_IPU_HSP_CLOCK 665000000 79259728Sray 80259728Sraystruct ipu3sc_softc { 81259728Sray device_t dev; 82259728Sray device_t sc_fbd; /* fbd child */ 83259728Sray struct fb_info sc_info; 84259728Sray 85259728Sray bus_space_tag_t iot; 86259728Sray bus_space_handle_t ioh; 87259728Sray bus_space_handle_t cm_ioh; 88259728Sray bus_space_handle_t dp_ioh; 89259728Sray bus_space_handle_t di0_ioh; 90259728Sray bus_space_handle_t di1_ioh; 91259728Sray bus_space_handle_t dctmpl_ioh; 92259728Sray bus_space_handle_t dc_ioh; 93259728Sray bus_space_handle_t dmfc_ioh; 94259728Sray bus_space_handle_t idmac_ioh; 95259728Sray bus_space_handle_t cpmem_ioh; 96259728Sray}; 97259728Sray 98259728Sraystatic struct ipu3sc_softc *ipu3sc_softc; 99259728Sray 100259728Sray#define IPUV3_READ(ipuv3, module, reg) \ 101259728Sray bus_space_read_4((ipuv3)->iot, (ipuv3)->module##_ioh, (reg)) 102259728Sray#define IPUV3_WRITE(ipuv3, module, reg, val) \ 103259728Sray bus_space_write_4((ipuv3)->iot, (ipuv3)->module##_ioh, (reg), (val)) 104259728Sray 105259728Sray#define CPMEM_CHANNEL_OFFSET(_c) ((_c) * 0x40) 106259728Sray#define CPMEM_WORD_OFFSET(_w) ((_w) * 0x20) 107259728Sray#define CPMEM_DP_OFFSET(_d) ((_d) * 0x10000) 108259728Sray#define IMX_IPU_DP0 0 109259728Sray#define IMX_IPU_DP1 1 110259728Sray#define CPMEM_CHANNEL(_dp, _ch, _w) \ 111259728Sray (CPMEM_DP_OFFSET(_dp) + CPMEM_CHANNEL_OFFSET(_ch) + \ 112259728Sray CPMEM_WORD_OFFSET(_w)) 113259728Sray#define CPMEM_OFFSET(_dp, _ch, _w, _o) \ 114259728Sray (CPMEM_CHANNEL((_dp), (_ch), (_w)) + (_o)) 115259728Sray 116259728Sraystatic int ipu3_fb_probe(device_t); 117259728Sraystatic int ipu3_fb_attach(device_t); 118259728Sray 119259728Sraystatic void 120259728Srayipu3_fb_init(struct ipu3sc_softc *sc) 121259728Sray{ 122259728Sray uint64_t w0sh96; 123259728Sray uint32_t w1sh96; 124259728Sray 125259728Sray /* FW W0[137:125] - 96 = [41:29] */ 126259728Sray /* FH W0[149:138] - 96 = [53:42] */ 127259728Sray w0sh96 = IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 0, 16)); 128259728Sray w0sh96 <<= 32; 129259728Sray w0sh96 |= IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 0, 12)); 130259728Sray 131259728Sray sc->sc_info.fb_width = ((w0sh96 >> 29) & 0x1fff) + 1; 132259728Sray sc->sc_info.fb_height = ((w0sh96 >> 42) & 0x0fff) + 1; 133259728Sray 134259728Sray /* SLY W1[115:102] - 96 = [19:6] */ 135259728Sray w1sh96 = IPUV3_READ(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 12)); 136259728Sray sc->sc_info.fb_stride = ((w1sh96 >> 6) & 0x3fff) + 1; 137259728Sray 138259728Sray printf("%dx%d [%d]\n", sc->sc_info.fb_width, sc->sc_info.fb_height, 139259728Sray sc->sc_info.fb_stride); 140259728Sray sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride; 141259728Sray 142259728Sray sc->sc_info.fb_vbase = (intptr_t)contigmalloc(sc->sc_info.fb_size, 143259728Sray M_DEVBUF, M_ZERO, 0, ~0, PAGE_SIZE, 0); 144259728Sray sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase); 145259728Sray 146259728Sray /* DP1 + config_ch_23 + word_2 */ 147259728Sray IPUV3_WRITE(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 0), 148259728Sray (((uint32_t)sc->sc_info.fb_pbase >> 3) | 149259728Sray (((uint32_t)sc->sc_info.fb_pbase >> 3) << 29)) & 0xffffffff); 150259728Sray 151259728Sray IPUV3_WRITE(sc, cpmem, CPMEM_OFFSET(IMX_IPU_DP1, 23, 1, 4), 152259728Sray (((uint32_t)sc->sc_info.fb_pbase >> 3) >> 3) & 0xffffffff); 153259728Sray 154259728Sray /* XXX: fetch or set it from/to IPU. */ 155259728Sray sc->sc_info.fb_bpp = sc->sc_info.fb_depth = sc->sc_info.fb_stride / 156259728Sray sc->sc_info.fb_width * 8; 157259728Sray} 158259728Sray 159259728Sray/* Use own color map, because of different RGB offset. */ 160259728Sraystatic int 161259728Srayipu3_fb_init_cmap(uint32_t *cmap, int bytespp) 162259728Sray{ 163259728Sray 164259728Sray switch (bytespp) { 165259728Sray case 8: 166270262Sdumbbell return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 167259728Sray 0x7, 5, 0x7, 2, 0x3, 0)); 168259728Sray case 15: 169270262Sdumbbell return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 170259728Sray 0x1f, 10, 0x1f, 5, 0x1f, 0)); 171259728Sray case 16: 172270262Sdumbbell return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 173259728Sray 0x1f, 11, 0x3f, 5, 0x1f, 0)); 174259728Sray case 24: 175259728Sray case 32: /* Ignore alpha. */ 176270262Sdumbbell return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 177270262Sdumbbell 0xff, 0, 0xff, 8, 0xff, 16)); 178259728Sray default: 179259728Sray return (1); 180259728Sray } 181259728Sray} 182259728Sray 183259728Sraystatic int 184259728Srayipu3_fb_probe(device_t dev) 185259728Sray{ 186259728Sray 187266152Sian if (!ofw_bus_status_okay(dev)) 188266152Sian return (ENXIO); 189266152Sian 190259728Sray if (!ofw_bus_is_compatible(dev, "fsl,ipu3")) 191259728Sray return (ENXIO); 192259728Sray 193266365Sian device_set_desc(dev, "i.MX5x Image Processing Unit v3 (FB)"); 194259728Sray 195259728Sray return (BUS_PROBE_DEFAULT); 196259728Sray} 197259728Sray 198259728Sraystatic int 199259728Srayipu3_fb_attach(device_t dev) 200259728Sray{ 201259728Sray struct ipu3sc_softc *sc = device_get_softc(dev); 202259728Sray bus_space_tag_t iot; 203259728Sray bus_space_handle_t ioh; 204266365Sian phandle_t node; 205266365Sian pcell_t reg; 206266365Sian int err; 207266365Sian uintptr_t base; 208259728Sray 209259728Sray ipu3sc_softc = sc; 210259728Sray 211266365Sian if (bootverbose) 212266365Sian device_printf(dev, "clock gate status is %d\n", 213266365Sian imx51_get_clk_gating(IMX51CLK_IPU_HSP_CLK_ROOT)); 214259728Sray 215259728Sray sc->dev = dev; 216259728Sray 217259728Sray sc = device_get_softc(dev); 218259728Sray sc->iot = iot = fdtbus_bs_tag; 219259728Sray 220266365Sian /* 221266365Sian * Retrieve the device address based on the start address in the 222266365Sian * DTS. The DTS for i.MX51 specifies 0x5e000000 as the first register 223266365Sian * address, so we just subtract IPU_CM_BASE to get the offset at which 224266365Sian * the IPU device was memory mapped. 225266365Sian * On i.MX53, the offset is 0. 226266365Sian */ 227266365Sian node = ofw_bus_get_node(dev); 228266365Sian if ((OF_getprop(node, "reg", ®, sizeof(reg))) <= 0) 229266365Sian base = 0; 230266365Sian else 231266365Sian base = fdt32_to_cpu(reg) - IPU_CM_BASE(0); 232259728Sray /* map controller registers */ 233266365Sian err = bus_space_map(iot, IPU_CM_BASE(base), IPU_CM_SIZE, 0, &ioh); 234259728Sray if (err) 235259728Sray goto fail_retarn_cm; 236259728Sray sc->cm_ioh = ioh; 237259728Sray 238259728Sray /* map Display Multi FIFO Controller registers */ 239266365Sian err = bus_space_map(iot, IPU_DMFC_BASE(base), IPU_DMFC_SIZE, 0, &ioh); 240259728Sray if (err) 241259728Sray goto fail_retarn_dmfc; 242259728Sray sc->dmfc_ioh = ioh; 243259728Sray 244259728Sray /* map Display Interface 0 registers */ 245266365Sian err = bus_space_map(iot, IPU_DI0_BASE(base), IPU_DI0_SIZE, 0, &ioh); 246259728Sray if (err) 247259728Sray goto fail_retarn_di0; 248259728Sray sc->di0_ioh = ioh; 249259728Sray 250259728Sray /* map Display Interface 1 registers */ 251266365Sian err = bus_space_map(iot, IPU_DI1_BASE(base), IPU_DI0_SIZE, 0, &ioh); 252259728Sray if (err) 253259728Sray goto fail_retarn_di1; 254259728Sray sc->di1_ioh = ioh; 255259728Sray 256259728Sray /* map Display Processor registers */ 257266365Sian err = bus_space_map(iot, IPU_DP_BASE(base), IPU_DP_SIZE, 0, &ioh); 258259728Sray if (err) 259259728Sray goto fail_retarn_dp; 260259728Sray sc->dp_ioh = ioh; 261259728Sray 262259728Sray /* map Display Controller registers */ 263266365Sian err = bus_space_map(iot, IPU_DC_BASE(base), IPU_DC_SIZE, 0, &ioh); 264259728Sray if (err) 265259728Sray goto fail_retarn_dc; 266259728Sray sc->dc_ioh = ioh; 267259728Sray 268259728Sray /* map Image DMA Controller registers */ 269266365Sian err = bus_space_map(iot, IPU_IDMAC_BASE(base), IPU_IDMAC_SIZE, 0, 270266365Sian &ioh); 271259728Sray if (err) 272259728Sray goto fail_retarn_idmac; 273259728Sray sc->idmac_ioh = ioh; 274259728Sray 275259728Sray /* map CPMEM registers */ 276266365Sian err = bus_space_map(iot, IPU_CPMEM_BASE(base), IPU_CPMEM_SIZE, 0, 277266365Sian &ioh); 278259728Sray if (err) 279259728Sray goto fail_retarn_cpmem; 280259728Sray sc->cpmem_ioh = ioh; 281259728Sray 282259728Sray /* map DCTEMPL registers */ 283266365Sian err = bus_space_map(iot, IPU_DCTMPL_BASE(base), IPU_DCTMPL_SIZE, 0, 284266365Sian &ioh); 285259728Sray if (err) 286259728Sray goto fail_retarn_dctmpl; 287259728Sray sc->dctmpl_ioh = ioh; 288259728Sray 289259728Sray#ifdef notyet 290259728Sray sc->ih = imx51_ipuv3_intr_establish(IMX51_INT_IPUV3, IPL_BIO, 291259728Sray ipuv3intr, sc); 292259728Sray if (sc->ih == NULL) { 293259728Sray device_printf(sc->dev, 294259728Sray "unable to establish interrupt at irq %d\n", 295259728Sray IMX51_INT_IPUV3); 296259728Sray return (ENXIO); 297259728Sray } 298259728Sray#endif 299259728Sray 300259728Sray /* 301259728Sray * We have to wait until interrupts are enabled. 302259728Sray * Mailbox relies on it to get data from VideoCore 303259728Sray */ 304259728Sray ipu3_fb_init(sc); 305259728Sray 306259728Sray sc->sc_info.fb_name = device_get_nameunit(dev); 307259728Sray 308259728Sray ipu3_fb_init_cmap(sc->sc_info.fb_cmap, sc->sc_info.fb_depth); 309259728Sray sc->sc_info.fb_cmsize = 16; 310259728Sray 311259728Sray /* Ask newbus to attach framebuffer device to me. */ 312259728Sray sc->sc_fbd = device_add_child(dev, "fbd", device_get_unit(dev)); 313259728Sray if (sc->sc_fbd == NULL) 314259728Sray device_printf(dev, "Can't attach fbd device\n"); 315259728Sray 316259728Sray return (bus_generic_attach(dev)); 317259728Sray 318259728Srayfail_retarn_dctmpl: 319259728Sray bus_space_unmap(sc->iot, sc->cpmem_ioh, IPU_CPMEM_SIZE); 320259728Srayfail_retarn_cpmem: 321259728Sray bus_space_unmap(sc->iot, sc->idmac_ioh, IPU_IDMAC_SIZE); 322259728Srayfail_retarn_idmac: 323259728Sray bus_space_unmap(sc->iot, sc->dc_ioh, IPU_DC_SIZE); 324259728Srayfail_retarn_dp: 325259728Sray bus_space_unmap(sc->iot, sc->dp_ioh, IPU_DP_SIZE); 326259728Srayfail_retarn_dc: 327259728Sray bus_space_unmap(sc->iot, sc->di1_ioh, IPU_DI1_SIZE); 328259728Srayfail_retarn_di1: 329259728Sray bus_space_unmap(sc->iot, sc->di0_ioh, IPU_DI0_SIZE); 330259728Srayfail_retarn_di0: 331259728Sray bus_space_unmap(sc->iot, sc->dmfc_ioh, IPU_DMFC_SIZE); 332259728Srayfail_retarn_dmfc: 333259728Sray bus_space_unmap(sc->iot, sc->dc_ioh, IPU_CM_SIZE); 334259728Srayfail_retarn_cm: 335259728Sray device_printf(sc->dev, 336259728Sray "failed to map registers (errno=%d)\n", err); 337259728Sray return (err); 338259728Sray} 339259728Sray 340259728Sraystatic struct fb_info * 341259728Srayipu3_fb_getinfo(device_t dev) 342259728Sray{ 343259728Sray struct ipu3sc_softc *sc = device_get_softc(dev); 344259728Sray 345259728Sray return (&sc->sc_info); 346259728Sray} 347259728Sray 348259728Sraystatic device_method_t ipu3_fb_methods[] = { 349259728Sray /* Device interface */ 350259728Sray DEVMETHOD(device_probe, ipu3_fb_probe), 351259728Sray DEVMETHOD(device_attach, ipu3_fb_attach), 352259728Sray 353259728Sray /* Framebuffer service methods */ 354259728Sray DEVMETHOD(fb_getinfo, ipu3_fb_getinfo), 355259728Sray { 0, 0 } 356259728Sray}; 357259728Sray 358259728Sraystatic devclass_t ipu3_fb_devclass; 359259728Sray 360259728Sraystatic driver_t ipu3_fb_driver = { 361259728Sray "fb", 362259728Sray ipu3_fb_methods, 363259728Sray sizeof(struct ipu3sc_softc), 364259728Sray}; 365259728Sray 366259728SrayDRIVER_MODULE(fb, simplebus, ipu3_fb_driver, ipu3_fb_devclass, 0, 0); 367