aml8726_fb.c revision 280905
1280905Sganbold/*- 2280905Sganbold * Copyright 2013-2014 John Wehle <john@feith.com> 3280905Sganbold * All rights reserved. 4280905Sganbold * 5280905Sganbold * Redistribution and use in source and binary forms, with or without 6280905Sganbold * modification, are permitted provided that the following conditions 7280905Sganbold * are met: 8280905Sganbold * 1. Redistributions of source code must retain the above copyright 9280905Sganbold * notice, this list of conditions and the following disclaimer. 10280905Sganbold * 2. Redistributions in binary form must reproduce the above copyright 11280905Sganbold * notice, this list of conditions and the following disclaimer in the 12280905Sganbold * documentation and/or other materials provided with the distribution. 13280905Sganbold * 14280905Sganbold * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15280905Sganbold * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16280905Sganbold * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17280905Sganbold * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18280905Sganbold * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19280905Sganbold * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20280905Sganbold * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21280905Sganbold * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22280905Sganbold * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23280905Sganbold * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24280905Sganbold * SUCH DAMAGE. 25280905Sganbold */ 26280905Sganbold 27280905Sganbold/* 28280905Sganbold * Amlogic aml8726 frame buffer driver. 29280905Sganbold * 30280905Sganbold * The current implementation has limited flexibility. 31280905Sganbold * For example only progressive scan is supported when 32280905Sganbold * using HDMI and the resolution / frame rate is not 33280905Sganbold * negotiated. 34280905Sganbold */ 35280905Sganbold 36280905Sganbold#include <sys/cdefs.h> 37280905Sganbold__FBSDID("$FreeBSD: head/sys/arm/amlogic/aml8726/aml8726_fb.c 280905 2015-03-31 11:50:46Z ganbold $"); 38280905Sganbold 39280905Sganbold#include <sys/param.h> 40280905Sganbold#include <sys/systm.h> 41280905Sganbold#include <sys/conf.h> 42280905Sganbold#include <sys/bus.h> 43280905Sganbold#include <sys/kernel.h> 44280905Sganbold#include <sys/module.h> 45280905Sganbold#include <sys/lock.h> 46280905Sganbold#include <sys/mutex.h> 47280905Sganbold#include <sys/resource.h> 48280905Sganbold#include <sys/rman.h> 49280905Sganbold 50280905Sganbold#include <sys/fbio.h> 51280905Sganbold 52280905Sganbold#include <machine/bus.h> 53280905Sganbold#include <machine/cpu.h> 54280905Sganbold#include <machine/fdt.h> 55280905Sganbold 56280905Sganbold#include <dev/fdt/fdt_common.h> 57280905Sganbold#include <dev/ofw/ofw_bus.h> 58280905Sganbold#include <dev/ofw/ofw_bus_subr.h> 59280905Sganbold 60280905Sganbold#include <dev/fb/fbreg.h> 61280905Sganbold#include <dev/vt/vt.h> 62280905Sganbold 63280905Sganbold#include <arm/amlogic/aml8726/aml8726_fb.h> 64280905Sganbold 65280905Sganbold#include "fb_if.h" 66280905Sganbold 67280905Sganbold 68280905Sganboldenum aml8726_fb_output { 69280905Sganbold aml8726_unknown_fb_output, 70280905Sganbold aml8726_cvbs_fb_output, 71280905Sganbold aml8726_hdmi_fb_output, 72280905Sganbold aml8726_lcd_fb_output 73280905Sganbold}; 74280905Sganbold 75280905Sganboldstruct aml8726_fb_clk { 76280905Sganbold uint32_t freq; 77280905Sganbold uint32_t video_pre; 78280905Sganbold uint32_t video_post; 79280905Sganbold uint32_t video_x; 80280905Sganbold uint32_t hdmi_tx; 81280905Sganbold uint32_t encp; 82280905Sganbold uint32_t enci; 83280905Sganbold uint32_t enct; 84280905Sganbold uint32_t encl; 85280905Sganbold uint32_t vdac0; 86280905Sganbold uint32_t vdac1; 87280905Sganbold}; 88280905Sganbold 89280905Sganboldstruct aml8726_fb_softc { 90280905Sganbold device_t dev; 91280905Sganbold struct resource *res[4]; 92280905Sganbold struct mtx mtx; 93280905Sganbold void *ih_cookie; 94280905Sganbold struct fb_info info; 95280905Sganbold enum aml8726_fb_output output; 96280905Sganbold struct aml8726_fb_clk clk; 97280905Sganbold}; 98280905Sganbold 99280905Sganboldstatic struct resource_spec aml8726_fb_spec[] = { 100280905Sganbold { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* CANVAS */ 101280905Sganbold { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* VIU */ 102280905Sganbold { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* VPP */ 103280905Sganbold { SYS_RES_IRQ, 1, RF_ACTIVE }, /* INT_VIU_VSYNC */ 104280905Sganbold { -1, 0 } 105280905Sganbold}; 106280905Sganbold 107280905Sganbold#define AML_FB_LOCK(sc) mtx_lock(&(sc)->mtx) 108280905Sganbold#define AML_FB_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 109280905Sganbold#define AML_FB_LOCK_INIT(sc) \ 110280905Sganbold mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ 111280905Sganbold "fb", MTX_DEF) 112280905Sganbold#define AML_FB_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); 113280905Sganbold 114280905Sganbold#define CAV_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) 115280905Sganbold#define CAV_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) 116280905Sganbold#define CAV_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ 117280905Sganbold (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 118280905Sganbold 119280905Sganbold#define VIU_WRITE_4(sc, reg, val) bus_write_4((sc)->res[1], reg, (val)) 120280905Sganbold#define VIU_READ_4(sc, reg) bus_read_4((sc)->res[1], reg) 121280905Sganbold 122280905Sganbold#define VPP_WRITE_4(sc, reg, val) bus_write_4((sc)->res[2], reg, (val)) 123280905Sganbold#define VPP_READ_4(sc, reg) bus_read_4((sc)->res[2], reg) 124280905Sganbold 125280905Sganbold#define CLK_WRITE_4(sc, reg, val) bus_write_4((sc)->res[X], reg, (val)) 126280905Sganbold#define CLK_READ_4(sc, reg) bus_read_4((sc)->res[X], reg) 127280905Sganbold 128280905Sganbold#define AML_FB_CLK_FREQ_SD 1080 129280905Sganbold#define AML_FB_CLK_FREQ_HD 1488 130280905Sganbold 131280905Sganboldstatic void 132280905Sganboldaml8726_fb_cfg_output(struct aml8726_fb_softc *sc) 133280905Sganbold{ 134280905Sganbold /* XXX */ 135280905Sganbold} 136280905Sganbold 137280905Sganboldstatic void 138280905Sganboldaml8726_fb_cfg_video(struct aml8726_fb_softc *sc) 139280905Sganbold{ 140280905Sganbold uint32_t value; 141280905Sganbold 142280905Sganbold /* 143280905Sganbold * basic initialization 144280905Sganbold * 145280905Sganbold * The fifo depth is in units of 8 so programming 32 146280905Sganbold * sets the depth to 256. 147280905Sganbold */ 148280905Sganbold 149280905Sganbold value = (32 << AML_VIU_OSD_FIFO_CTRL_DEPTH_SHIFT); 150280905Sganbold value |= AML_VIU_OSD_FIFO_CTRL_BURST_LEN_64; 151280905Sganbold value |= (4 << AML_VIU_OSD_FIFO_CTRL_HOLD_LINES_SHIFT); 152280905Sganbold 153280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_FIFO_CTRL_REG, value); 154280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD2_FIFO_CTRL_REG, value); 155280905Sganbold 156280905Sganbold value = VPP_READ_4(sc, AML_VPP_MISC_REG); 157280905Sganbold 158280905Sganbold value &= ~AML_VPP_MISC_PREBLEND_EN; 159280905Sganbold value |= AML_VPP_MISC_POSTBLEND_EN; 160280905Sganbold value &= ~(AML_VPP_MISC_OSD1_POSTBLEND | AML_VPP_MISC_OSD2_POSTBLEND 161280905Sganbold | AML_VPP_MISC_VD1_POSTBLEND | AML_VPP_MISC_VD2_POSTBLEND); 162280905Sganbold 163280905Sganbold VPP_WRITE_4(sc, AML_VPP_MISC_REG, value); 164280905Sganbold 165280905Sganbold value = AML_VIU_OSD_CTRL_OSD_EN; 166280905Sganbold value |= (0xff << AML_VIU_OSD_CTRL_GLOBAL_ALPHA_SHIFT); 167280905Sganbold 168280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value); 169280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD2_CTRL_REG, value); 170280905Sganbold 171280905Sganbold /* color mode for OSD1 block 0 */ 172280905Sganbold 173280905Sganbold value = (AML_CAV_OSD1_INDEX << AML_VIU_OSD_BLK_CFG_W0_INDEX_SHIFT) 174280905Sganbold | AML_VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN 175280905Sganbold | AML_VIU_OSD_BLK_CFG_W0_BLKMODE_24 176280905Sganbold | AML_VIU_OSD_BLK_CFG_W0_RGB_EN 177280905Sganbold | AML_VIU_OSD_BLK_CFG_W0_CMATRIX_RGB; 178280905Sganbold 179280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W0_REG, value); 180280905Sganbold 181280905Sganbold /* geometry / scaling for OSD1 block 0 */ 182280905Sganbold 183280905Sganbold value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W1_X_END_SHIFT) 184280905Sganbold & AML_VIU_OSD_BLK_CFG_W1_X_END_MASK; 185280905Sganbold value |= (0 << AML_VIU_OSD_BLK_CFG_W1_X_START_SHIFT) 186280905Sganbold & AML_VIU_OSD_BLK_CFG_W1_X_START_MASK; 187280905Sganbold 188280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W1_REG, value); 189280905Sganbold 190280905Sganbold value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W2_Y_END_SHIFT) 191280905Sganbold & AML_VIU_OSD_BLK_CFG_W2_Y_END_MASK; 192280905Sganbold value |= (0 << AML_VIU_OSD_BLK_CFG_W2_Y_START_SHIFT) 193280905Sganbold & AML_VIU_OSD_BLK_CFG_W2_Y_START_MASK; 194280905Sganbold 195280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W2_REG, value); 196280905Sganbold 197280905Sganbold value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W3_H_END_SHIFT) 198280905Sganbold & AML_VIU_OSD_BLK_CFG_W3_H_END_MASK; 199280905Sganbold value |= (0 << AML_VIU_OSD_BLK_CFG_W3_H_START_SHIFT) 200280905Sganbold & AML_VIU_OSD_BLK_CFG_W3_H_START_MASK; 201280905Sganbold 202280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W3_REG, value); 203280905Sganbold 204280905Sganbold value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W4_V_END_SHIFT) 205280905Sganbold & AML_VIU_OSD_BLK_CFG_W4_V_END_MASK; 206280905Sganbold value |= (0 << AML_VIU_OSD_BLK_CFG_W4_V_START_SHIFT) 207280905Sganbold & AML_VIU_OSD_BLK_CFG_W4_V_START_MASK; 208280905Sganbold 209280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W4_REG, value); 210280905Sganbold 211280905Sganbold /* Enable the OSD block now that it's fully configured */ 212280905Sganbold 213280905Sganbold value = VIU_READ_4(sc, AML_VIU_OSD1_CTRL_REG); 214280905Sganbold 215280905Sganbold value &= ~AML_VIU_OSD_CTRL_OSD_BLK_EN_MASK; 216280905Sganbold value |= 1 << AML_VIU_OSD_CTRL_OSD_BLK_EN_SHIFT; 217280905Sganbold 218280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value); 219280905Sganbold 220280905Sganbold /* enable video processing of OSD1 */ 221280905Sganbold 222280905Sganbold value = VPP_READ_4(sc, AML_VPP_MISC_REG); 223280905Sganbold 224280905Sganbold value |= AML_VPP_MISC_OSD1_POSTBLEND; 225280905Sganbold 226280905Sganbold VPP_WRITE_4(sc, AML_VPP_MISC_REG, value); 227280905Sganbold} 228280905Sganbold 229280905Sganboldstatic void 230280905Sganboldaml8726_fb_cfg_canvas(struct aml8726_fb_softc *sc) 231280905Sganbold{ 232280905Sganbold uint32_t value; 233280905Sganbold uint32_t width; 234280905Sganbold 235280905Sganbold /* 236280905Sganbold * The frame buffer address and width are programmed in units of 8 237280905Sganbold * (meaning they need to be aligned and the actual values divided 238280905Sganbold * by 8 prior to programming the hardware). 239280905Sganbold */ 240280905Sganbold 241280905Sganbold width = (uint32_t)sc->info.fb_stride / 8; 242280905Sganbold 243280905Sganbold /* lower bits of the width */ 244280905Sganbold value = (width << AML_CAV_LUT_DATAL_WIDTH_SHIFT) & 245280905Sganbold AML_CAV_LUT_DATAL_WIDTH_MASK; 246280905Sganbold 247280905Sganbold /* physical address */ 248280905Sganbold value |= (uint32_t)sc->info.fb_pbase / 8; 249280905Sganbold 250280905Sganbold CAV_WRITE_4(sc, AML_CAV_LUT_DATAL_REG, value); 251280905Sganbold 252280905Sganbold /* upper bits of the width */ 253280905Sganbold value = ((width >> AML_CAV_LUT_DATAL_WIDTH_WIDTH) << 254280905Sganbold AML_CAV_LUT_DATAH_WIDTH_SHIFT) & AML_CAV_LUT_DATAH_WIDTH_MASK; 255280905Sganbold 256280905Sganbold /* height */ 257280905Sganbold value |= ((uint32_t)sc->info.fb_height << 258280905Sganbold AML_CAV_LUT_DATAH_HEIGHT_SHIFT) & AML_CAV_LUT_DATAH_HEIGHT_MASK; 259280905Sganbold 260280905Sganbold /* mode */ 261280905Sganbold value |= AML_CAV_LUT_DATAH_BLKMODE_LINEAR; 262280905Sganbold 263280905Sganbold CAV_WRITE_4(sc, AML_CAV_LUT_DATAH_REG, value); 264280905Sganbold 265280905Sganbold CAV_WRITE_4(sc, AML_CAV_LUT_ADDR_REG, (AML_CAV_LUT_ADDR_WR_EN | 266280905Sganbold (AML_CAV_OSD1_INDEX << AML_CAV_LUT_ADDR_INDEX_SHIFT))); 267280905Sganbold 268280905Sganbold CAV_BARRIER(sc, AML_CAV_LUT_ADDR_REG); 269280905Sganbold} 270280905Sganbold 271280905Sganboldstatic void 272280905Sganboldaml8726_fb_intr(void *arg) 273280905Sganbold{ 274280905Sganbold struct aml8726_fb_softc *sc = (struct aml8726_fb_softc *)arg; 275280905Sganbold 276280905Sganbold AML_FB_LOCK(sc); 277280905Sganbold 278280905Sganbold AML_FB_UNLOCK(sc); 279280905Sganbold} 280280905Sganbold 281280905Sganboldstatic int 282280905Sganboldaml8726_fb_probe(device_t dev) 283280905Sganbold{ 284280905Sganbold 285280905Sganbold if (!ofw_bus_status_okay(dev)) 286280905Sganbold return (ENXIO); 287280905Sganbold 288280905Sganbold if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-fb")) 289280905Sganbold return (ENXIO); 290280905Sganbold 291280905Sganbold device_set_desc(dev, "Amlogic aml8726 FB"); 292280905Sganbold 293280905Sganbold return (BUS_PROBE_DEFAULT); 294280905Sganbold} 295280905Sganbold 296280905Sganboldstatic int 297280905Sganboldaml8726_fb_attach(device_t dev) 298280905Sganbold{ 299280905Sganbold struct aml8726_fb_softc *sc = device_get_softc(dev); 300280905Sganbold int error; 301280905Sganbold device_t child; 302280905Sganbold pcell_t prop; 303280905Sganbold phandle_t node; 304280905Sganbold 305280905Sganbold sc->dev = dev; 306280905Sganbold 307280905Sganbold sc->info.fb_name = device_get_nameunit(sc->dev); 308280905Sganbold 309280905Sganbold node = ofw_bus_get_node(dev); 310280905Sganbold 311280905Sganbold if (OF_getencprop(node, "width", &prop, sizeof(prop)) <= 0) { 312280905Sganbold device_printf(dev, "missing width attribute in FDT\n"); 313280905Sganbold return (ENXIO); 314280905Sganbold } 315280905Sganbold if ((prop % 8) != 0) { 316280905Sganbold device_printf(dev, 317280905Sganbold "width attribute in FDT must be a multiple of 8\n"); 318280905Sganbold return (ENXIO); 319280905Sganbold } 320280905Sganbold sc->info.fb_width = prop; 321280905Sganbold 322280905Sganbold if (OF_getencprop(node, "height", &prop, sizeof(prop)) <= 0) { 323280905Sganbold device_printf(dev, "missing height attribute in FDT\n"); 324280905Sganbold return (ENXIO); 325280905Sganbold } 326280905Sganbold sc->info.fb_height = prop; 327280905Sganbold 328280905Sganbold if (OF_getencprop(node, "depth", &prop, sizeof(prop)) <= 0) { 329280905Sganbold device_printf(dev, "missing depth attribute in FDT\n"); 330280905Sganbold return (ENXIO); 331280905Sganbold } 332280905Sganbold if (prop != 24) { 333280905Sganbold device_printf(dev, 334280905Sganbold "depth attribute in FDT is an unsupported value\n"); 335280905Sganbold return (ENXIO); 336280905Sganbold } 337280905Sganbold sc->info.fb_depth = prop; 338280905Sganbold sc->info.fb_bpp = prop; 339280905Sganbold 340280905Sganbold if (OF_getencprop(node, "linebytes", &prop, sizeof(prop)) <= 0) { 341280905Sganbold device_printf(dev, "missing linebytes attribute in FDT\n"); 342280905Sganbold return (ENXIO); 343280905Sganbold } 344280905Sganbold if ((prop % 8) != 0) { 345280905Sganbold device_printf(dev, 346280905Sganbold "linebytes attribute in FDT must be a multiple of 8\n"); 347280905Sganbold return (ENXIO); 348280905Sganbold } 349280905Sganbold if (prop < (sc->info.fb_width * 3)) { 350280905Sganbold device_printf(dev, 351280905Sganbold "linebytes attribute in FDT is too small\n"); 352280905Sganbold return (ENXIO); 353280905Sganbold } 354280905Sganbold sc->info.fb_stride = prop; 355280905Sganbold 356280905Sganbold if (OF_getencprop(node, "address", &prop, sizeof(prop)) <= 0) { 357280905Sganbold device_printf(dev, "missing address attribute in FDT\n"); 358280905Sganbold return (ENXIO); 359280905Sganbold } 360280905Sganbold if ((prop % 8) != 0) { 361280905Sganbold device_printf(dev, 362280905Sganbold "address attribute in FDT must be a multiple of 8\n"); 363280905Sganbold return (ENXIO); 364280905Sganbold } 365280905Sganbold sc->info.fb_pbase = prop; 366280905Sganbold sc->info.fb_size = sc->info.fb_height * sc->info.fb_stride; 367280905Sganbold sc->info.fb_vbase = (intptr_t)pmap_mapdev(sc->info.fb_pbase, 368280905Sganbold sc->info.fb_size); 369280905Sganbold 370280905Sganbold if (bus_alloc_resources(dev, aml8726_fb_spec, sc->res)) { 371280905Sganbold device_printf(dev, "could not allocate resources for device\n"); 372280905Sganbold pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); 373280905Sganbold return (ENXIO); 374280905Sganbold } 375280905Sganbold 376280905Sganbold aml8726_fb_cfg_output(sc); 377280905Sganbold 378280905Sganbold aml8726_fb_cfg_video(sc); 379280905Sganbold 380280905Sganbold aml8726_fb_cfg_canvas(sc); 381280905Sganbold 382280905Sganbold AML_FB_LOCK_INIT(sc); 383280905Sganbold 384280905Sganbold error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE, 385280905Sganbold NULL, aml8726_fb_intr, sc, &sc->ih_cookie); 386280905Sganbold 387280905Sganbold if (error) { 388280905Sganbold device_printf(dev, "could not setup interrupt handler\n"); 389280905Sganbold goto fail; 390280905Sganbold } 391280905Sganbold 392280905Sganbold child = device_add_child(dev, "fbd", device_get_unit(dev)); 393280905Sganbold 394280905Sganbold if (!child) { 395280905Sganbold device_printf(dev, "could not add fbd\n"); 396280905Sganbold error = ENXIO; 397280905Sganbold goto fail; 398280905Sganbold } 399280905Sganbold 400280905Sganbold error = device_probe_and_attach(child); 401280905Sganbold 402280905Sganbold if (error) { 403280905Sganbold device_printf(dev, "could not attach fbd\n"); 404280905Sganbold goto fail; 405280905Sganbold } 406280905Sganbold 407280905Sganbold return (0); 408280905Sganbold 409280905Sganboldfail: 410280905Sganbold if (sc->ih_cookie) 411280905Sganbold bus_teardown_intr(dev, sc->res[3], sc->ih_cookie); 412280905Sganbold 413280905Sganbold AML_FB_LOCK_DESTROY(sc); 414280905Sganbold 415280905Sganbold bus_release_resources(dev, aml8726_fb_spec, sc->res); 416280905Sganbold 417280905Sganbold pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); 418280905Sganbold 419280905Sganbold return (error); 420280905Sganbold} 421280905Sganbold 422280905Sganboldstatic int 423280905Sganboldaml8726_fb_detach(device_t dev) 424280905Sganbold{ 425280905Sganbold struct aml8726_fb_softc *sc = device_get_softc(dev); 426280905Sganbold 427280905Sganbold bus_generic_detach(dev); 428280905Sganbold 429280905Sganbold bus_teardown_intr(dev, sc->res[3], sc->ih_cookie); 430280905Sganbold 431280905Sganbold AML_FB_LOCK_DESTROY(sc); 432280905Sganbold 433280905Sganbold bus_release_resources(dev, aml8726_fb_spec, sc->res); 434280905Sganbold 435280905Sganbold pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); 436280905Sganbold 437280905Sganbold return (0); 438280905Sganbold} 439280905Sganbold 440280905Sganboldstatic struct fb_info * 441280905Sganboldaml8726_fb_getinfo(device_t dev) 442280905Sganbold{ 443280905Sganbold struct aml8726_fb_softc *sc = device_get_softc(dev); 444280905Sganbold 445280905Sganbold return (&sc->info); 446280905Sganbold} 447280905Sganbold 448280905Sganboldstatic device_method_t aml8726_fb_methods[] = { 449280905Sganbold /* Device interface */ 450280905Sganbold DEVMETHOD(device_probe, aml8726_fb_probe), 451280905Sganbold DEVMETHOD(device_attach, aml8726_fb_attach), 452280905Sganbold DEVMETHOD(device_detach, aml8726_fb_detach), 453280905Sganbold 454280905Sganbold /* FB interface */ 455280905Sganbold DEVMETHOD(fb_getinfo, aml8726_fb_getinfo), 456280905Sganbold 457280905Sganbold DEVMETHOD_END 458280905Sganbold}; 459280905Sganbold 460280905Sganboldstatic driver_t aml8726_fb_driver = { 461280905Sganbold "fb", 462280905Sganbold aml8726_fb_methods, 463280905Sganbold sizeof(struct aml8726_fb_softc), 464280905Sganbold}; 465280905Sganbold 466280905Sganboldstatic devclass_t aml8726_fb_devclass; 467280905Sganbold 468280905SganboldDRIVER_MODULE(fb, simplebus, aml8726_fb_driver, aml8726_fb_devclass, 0, 0); 469