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$"); 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 52281092Sandrew#include <vm/vm.h> 53281092Sandrew#include <vm/pmap.h> 54281092Sandrew 55280905Sganbold#include <machine/bus.h> 56280905Sganbold#include <machine/cpu.h> 57280905Sganbold#include <machine/fdt.h> 58280905Sganbold 59280905Sganbold#include <dev/fdt/fdt_common.h> 60280905Sganbold#include <dev/ofw/ofw_bus.h> 61280905Sganbold#include <dev/ofw/ofw_bus_subr.h> 62280905Sganbold 63280905Sganbold#include <dev/fb/fbreg.h> 64280905Sganbold#include <dev/vt/vt.h> 65280905Sganbold 66280905Sganbold#include <arm/amlogic/aml8726/aml8726_fb.h> 67280905Sganbold 68280905Sganbold#include "fb_if.h" 69280905Sganbold 70280905Sganbold 71280905Sganboldenum aml8726_fb_output { 72280905Sganbold aml8726_unknown_fb_output, 73280905Sganbold aml8726_cvbs_fb_output, 74280905Sganbold aml8726_hdmi_fb_output, 75280905Sganbold aml8726_lcd_fb_output 76280905Sganbold}; 77280905Sganbold 78280905Sganboldstruct aml8726_fb_clk { 79280905Sganbold uint32_t freq; 80280905Sganbold uint32_t video_pre; 81280905Sganbold uint32_t video_post; 82280905Sganbold uint32_t video_x; 83280905Sganbold uint32_t hdmi_tx; 84280905Sganbold uint32_t encp; 85280905Sganbold uint32_t enci; 86280905Sganbold uint32_t enct; 87280905Sganbold uint32_t encl; 88280905Sganbold uint32_t vdac0; 89280905Sganbold uint32_t vdac1; 90280905Sganbold}; 91280905Sganbold 92280905Sganboldstruct aml8726_fb_softc { 93280905Sganbold device_t dev; 94280905Sganbold struct resource *res[4]; 95280905Sganbold struct mtx mtx; 96280905Sganbold void *ih_cookie; 97280905Sganbold struct fb_info info; 98280905Sganbold enum aml8726_fb_output output; 99280905Sganbold struct aml8726_fb_clk clk; 100280905Sganbold}; 101280905Sganbold 102280905Sganboldstatic struct resource_spec aml8726_fb_spec[] = { 103280905Sganbold { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* CANVAS */ 104280905Sganbold { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* VIU */ 105280905Sganbold { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* VPP */ 106280905Sganbold { SYS_RES_IRQ, 1, RF_ACTIVE }, /* INT_VIU_VSYNC */ 107280905Sganbold { -1, 0 } 108280905Sganbold}; 109280905Sganbold 110280905Sganbold#define AML_FB_LOCK(sc) mtx_lock(&(sc)->mtx) 111280905Sganbold#define AML_FB_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 112280905Sganbold#define AML_FB_LOCK_INIT(sc) \ 113280905Sganbold mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ 114280905Sganbold "fb", MTX_DEF) 115280905Sganbold#define AML_FB_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); 116280905Sganbold 117280905Sganbold#define CAV_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) 118280905Sganbold#define CAV_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) 119280905Sganbold#define CAV_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ 120280905Sganbold (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 121280905Sganbold 122280905Sganbold#define VIU_WRITE_4(sc, reg, val) bus_write_4((sc)->res[1], reg, (val)) 123280905Sganbold#define VIU_READ_4(sc, reg) bus_read_4((sc)->res[1], reg) 124280905Sganbold 125280905Sganbold#define VPP_WRITE_4(sc, reg, val) bus_write_4((sc)->res[2], reg, (val)) 126280905Sganbold#define VPP_READ_4(sc, reg) bus_read_4((sc)->res[2], reg) 127280905Sganbold 128280905Sganbold#define CLK_WRITE_4(sc, reg, val) bus_write_4((sc)->res[X], reg, (val)) 129280905Sganbold#define CLK_READ_4(sc, reg) bus_read_4((sc)->res[X], reg) 130280905Sganbold 131280905Sganbold#define AML_FB_CLK_FREQ_SD 1080 132280905Sganbold#define AML_FB_CLK_FREQ_HD 1488 133280905Sganbold 134280905Sganboldstatic void 135280905Sganboldaml8726_fb_cfg_output(struct aml8726_fb_softc *sc) 136280905Sganbold{ 137280905Sganbold /* XXX */ 138280905Sganbold} 139280905Sganbold 140280905Sganboldstatic void 141280905Sganboldaml8726_fb_cfg_video(struct aml8726_fb_softc *sc) 142280905Sganbold{ 143280905Sganbold uint32_t value; 144280905Sganbold 145280905Sganbold /* 146280905Sganbold * basic initialization 147280905Sganbold * 148280905Sganbold * The fifo depth is in units of 8 so programming 32 149280905Sganbold * sets the depth to 256. 150280905Sganbold */ 151280905Sganbold 152280905Sganbold value = (32 << AML_VIU_OSD_FIFO_CTRL_DEPTH_SHIFT); 153280905Sganbold value |= AML_VIU_OSD_FIFO_CTRL_BURST_LEN_64; 154280905Sganbold value |= (4 << AML_VIU_OSD_FIFO_CTRL_HOLD_LINES_SHIFT); 155280905Sganbold 156280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_FIFO_CTRL_REG, value); 157280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD2_FIFO_CTRL_REG, value); 158280905Sganbold 159280905Sganbold value = VPP_READ_4(sc, AML_VPP_MISC_REG); 160280905Sganbold 161280905Sganbold value &= ~AML_VPP_MISC_PREBLEND_EN; 162280905Sganbold value |= AML_VPP_MISC_POSTBLEND_EN; 163280905Sganbold value &= ~(AML_VPP_MISC_OSD1_POSTBLEND | AML_VPP_MISC_OSD2_POSTBLEND 164280905Sganbold | AML_VPP_MISC_VD1_POSTBLEND | AML_VPP_MISC_VD2_POSTBLEND); 165280905Sganbold 166280905Sganbold VPP_WRITE_4(sc, AML_VPP_MISC_REG, value); 167280905Sganbold 168280905Sganbold value = AML_VIU_OSD_CTRL_OSD_EN; 169280905Sganbold value |= (0xff << AML_VIU_OSD_CTRL_GLOBAL_ALPHA_SHIFT); 170280905Sganbold 171280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value); 172280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD2_CTRL_REG, value); 173280905Sganbold 174280905Sganbold /* color mode for OSD1 block 0 */ 175280905Sganbold 176280905Sganbold value = (AML_CAV_OSD1_INDEX << AML_VIU_OSD_BLK_CFG_W0_INDEX_SHIFT) 177280905Sganbold | AML_VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN 178280905Sganbold | AML_VIU_OSD_BLK_CFG_W0_BLKMODE_24 179280905Sganbold | AML_VIU_OSD_BLK_CFG_W0_RGB_EN 180280905Sganbold | AML_VIU_OSD_BLK_CFG_W0_CMATRIX_RGB; 181280905Sganbold 182280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W0_REG, value); 183280905Sganbold 184280905Sganbold /* geometry / scaling for OSD1 block 0 */ 185280905Sganbold 186280905Sganbold value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W1_X_END_SHIFT) 187280905Sganbold & AML_VIU_OSD_BLK_CFG_W1_X_END_MASK; 188280905Sganbold value |= (0 << AML_VIU_OSD_BLK_CFG_W1_X_START_SHIFT) 189280905Sganbold & AML_VIU_OSD_BLK_CFG_W1_X_START_MASK; 190280905Sganbold 191280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W1_REG, value); 192280905Sganbold 193280905Sganbold value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W2_Y_END_SHIFT) 194280905Sganbold & AML_VIU_OSD_BLK_CFG_W2_Y_END_MASK; 195280905Sganbold value |= (0 << AML_VIU_OSD_BLK_CFG_W2_Y_START_SHIFT) 196280905Sganbold & AML_VIU_OSD_BLK_CFG_W2_Y_START_MASK; 197280905Sganbold 198280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W2_REG, value); 199280905Sganbold 200280905Sganbold value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W3_H_END_SHIFT) 201280905Sganbold & AML_VIU_OSD_BLK_CFG_W3_H_END_MASK; 202280905Sganbold value |= (0 << AML_VIU_OSD_BLK_CFG_W3_H_START_SHIFT) 203280905Sganbold & AML_VIU_OSD_BLK_CFG_W3_H_START_MASK; 204280905Sganbold 205280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W3_REG, value); 206280905Sganbold 207280905Sganbold value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W4_V_END_SHIFT) 208280905Sganbold & AML_VIU_OSD_BLK_CFG_W4_V_END_MASK; 209280905Sganbold value |= (0 << AML_VIU_OSD_BLK_CFG_W4_V_START_SHIFT) 210280905Sganbold & AML_VIU_OSD_BLK_CFG_W4_V_START_MASK; 211280905Sganbold 212280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W4_REG, value); 213280905Sganbold 214280905Sganbold /* Enable the OSD block now that it's fully configured */ 215280905Sganbold 216280905Sganbold value = VIU_READ_4(sc, AML_VIU_OSD1_CTRL_REG); 217280905Sganbold 218280905Sganbold value &= ~AML_VIU_OSD_CTRL_OSD_BLK_EN_MASK; 219280905Sganbold value |= 1 << AML_VIU_OSD_CTRL_OSD_BLK_EN_SHIFT; 220280905Sganbold 221280905Sganbold VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value); 222280905Sganbold 223280905Sganbold /* enable video processing of OSD1 */ 224280905Sganbold 225280905Sganbold value = VPP_READ_4(sc, AML_VPP_MISC_REG); 226280905Sganbold 227280905Sganbold value |= AML_VPP_MISC_OSD1_POSTBLEND; 228280905Sganbold 229280905Sganbold VPP_WRITE_4(sc, AML_VPP_MISC_REG, value); 230280905Sganbold} 231280905Sganbold 232280905Sganboldstatic void 233280905Sganboldaml8726_fb_cfg_canvas(struct aml8726_fb_softc *sc) 234280905Sganbold{ 235280905Sganbold uint32_t value; 236280905Sganbold uint32_t width; 237280905Sganbold 238280905Sganbold /* 239280905Sganbold * The frame buffer address and width are programmed in units of 8 240280905Sganbold * (meaning they need to be aligned and the actual values divided 241280905Sganbold * by 8 prior to programming the hardware). 242280905Sganbold */ 243280905Sganbold 244280905Sganbold width = (uint32_t)sc->info.fb_stride / 8; 245280905Sganbold 246280905Sganbold /* lower bits of the width */ 247280905Sganbold value = (width << AML_CAV_LUT_DATAL_WIDTH_SHIFT) & 248280905Sganbold AML_CAV_LUT_DATAL_WIDTH_MASK; 249280905Sganbold 250280905Sganbold /* physical address */ 251280905Sganbold value |= (uint32_t)sc->info.fb_pbase / 8; 252280905Sganbold 253280905Sganbold CAV_WRITE_4(sc, AML_CAV_LUT_DATAL_REG, value); 254280905Sganbold 255280905Sganbold /* upper bits of the width */ 256280905Sganbold value = ((width >> AML_CAV_LUT_DATAL_WIDTH_WIDTH) << 257280905Sganbold AML_CAV_LUT_DATAH_WIDTH_SHIFT) & AML_CAV_LUT_DATAH_WIDTH_MASK; 258280905Sganbold 259280905Sganbold /* height */ 260280905Sganbold value |= ((uint32_t)sc->info.fb_height << 261280905Sganbold AML_CAV_LUT_DATAH_HEIGHT_SHIFT) & AML_CAV_LUT_DATAH_HEIGHT_MASK; 262280905Sganbold 263280905Sganbold /* mode */ 264280905Sganbold value |= AML_CAV_LUT_DATAH_BLKMODE_LINEAR; 265280905Sganbold 266280905Sganbold CAV_WRITE_4(sc, AML_CAV_LUT_DATAH_REG, value); 267280905Sganbold 268280905Sganbold CAV_WRITE_4(sc, AML_CAV_LUT_ADDR_REG, (AML_CAV_LUT_ADDR_WR_EN | 269280905Sganbold (AML_CAV_OSD1_INDEX << AML_CAV_LUT_ADDR_INDEX_SHIFT))); 270280905Sganbold 271280905Sganbold CAV_BARRIER(sc, AML_CAV_LUT_ADDR_REG); 272280905Sganbold} 273280905Sganbold 274280905Sganboldstatic void 275280905Sganboldaml8726_fb_intr(void *arg) 276280905Sganbold{ 277280905Sganbold struct aml8726_fb_softc *sc = (struct aml8726_fb_softc *)arg; 278280905Sganbold 279280905Sganbold AML_FB_LOCK(sc); 280280905Sganbold 281280905Sganbold AML_FB_UNLOCK(sc); 282280905Sganbold} 283280905Sganbold 284280905Sganboldstatic int 285280905Sganboldaml8726_fb_probe(device_t dev) 286280905Sganbold{ 287280905Sganbold 288280905Sganbold if (!ofw_bus_status_okay(dev)) 289280905Sganbold return (ENXIO); 290280905Sganbold 291280905Sganbold if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-fb")) 292280905Sganbold return (ENXIO); 293280905Sganbold 294280905Sganbold device_set_desc(dev, "Amlogic aml8726 FB"); 295280905Sganbold 296280905Sganbold return (BUS_PROBE_DEFAULT); 297280905Sganbold} 298280905Sganbold 299280905Sganboldstatic int 300280905Sganboldaml8726_fb_attach(device_t dev) 301280905Sganbold{ 302280905Sganbold struct aml8726_fb_softc *sc = device_get_softc(dev); 303280905Sganbold int error; 304280905Sganbold device_t child; 305280905Sganbold pcell_t prop; 306280905Sganbold phandle_t node; 307280905Sganbold 308280905Sganbold sc->dev = dev; 309280905Sganbold 310280905Sganbold sc->info.fb_name = device_get_nameunit(sc->dev); 311280905Sganbold 312280905Sganbold node = ofw_bus_get_node(dev); 313280905Sganbold 314280905Sganbold if (OF_getencprop(node, "width", &prop, sizeof(prop)) <= 0) { 315280905Sganbold device_printf(dev, "missing width attribute in FDT\n"); 316280905Sganbold return (ENXIO); 317280905Sganbold } 318280905Sganbold if ((prop % 8) != 0) { 319280905Sganbold device_printf(dev, 320280905Sganbold "width attribute in FDT must be a multiple of 8\n"); 321280905Sganbold return (ENXIO); 322280905Sganbold } 323280905Sganbold sc->info.fb_width = prop; 324280905Sganbold 325280905Sganbold if (OF_getencprop(node, "height", &prop, sizeof(prop)) <= 0) { 326280905Sganbold device_printf(dev, "missing height attribute in FDT\n"); 327280905Sganbold return (ENXIO); 328280905Sganbold } 329280905Sganbold sc->info.fb_height = prop; 330280905Sganbold 331280905Sganbold if (OF_getencprop(node, "depth", &prop, sizeof(prop)) <= 0) { 332280905Sganbold device_printf(dev, "missing depth attribute in FDT\n"); 333280905Sganbold return (ENXIO); 334280905Sganbold } 335280905Sganbold if (prop != 24) { 336280905Sganbold device_printf(dev, 337280905Sganbold "depth attribute in FDT is an unsupported value\n"); 338280905Sganbold return (ENXIO); 339280905Sganbold } 340280905Sganbold sc->info.fb_depth = prop; 341280905Sganbold sc->info.fb_bpp = prop; 342280905Sganbold 343280905Sganbold if (OF_getencprop(node, "linebytes", &prop, sizeof(prop)) <= 0) { 344280905Sganbold device_printf(dev, "missing linebytes attribute in FDT\n"); 345280905Sganbold return (ENXIO); 346280905Sganbold } 347280905Sganbold if ((prop % 8) != 0) { 348280905Sganbold device_printf(dev, 349280905Sganbold "linebytes attribute in FDT must be a multiple of 8\n"); 350280905Sganbold return (ENXIO); 351280905Sganbold } 352280905Sganbold if (prop < (sc->info.fb_width * 3)) { 353280905Sganbold device_printf(dev, 354280905Sganbold "linebytes attribute in FDT is too small\n"); 355280905Sganbold return (ENXIO); 356280905Sganbold } 357280905Sganbold sc->info.fb_stride = prop; 358280905Sganbold 359280905Sganbold if (OF_getencprop(node, "address", &prop, sizeof(prop)) <= 0) { 360280905Sganbold device_printf(dev, "missing address attribute in FDT\n"); 361280905Sganbold return (ENXIO); 362280905Sganbold } 363280905Sganbold if ((prop % 8) != 0) { 364280905Sganbold device_printf(dev, 365280905Sganbold "address attribute in FDT must be a multiple of 8\n"); 366280905Sganbold return (ENXIO); 367280905Sganbold } 368280905Sganbold sc->info.fb_pbase = prop; 369280905Sganbold sc->info.fb_size = sc->info.fb_height * sc->info.fb_stride; 370280905Sganbold sc->info.fb_vbase = (intptr_t)pmap_mapdev(sc->info.fb_pbase, 371280905Sganbold sc->info.fb_size); 372280905Sganbold 373280905Sganbold if (bus_alloc_resources(dev, aml8726_fb_spec, sc->res)) { 374280905Sganbold device_printf(dev, "could not allocate resources for device\n"); 375280905Sganbold pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); 376280905Sganbold return (ENXIO); 377280905Sganbold } 378280905Sganbold 379280905Sganbold aml8726_fb_cfg_output(sc); 380280905Sganbold 381280905Sganbold aml8726_fb_cfg_video(sc); 382280905Sganbold 383280905Sganbold aml8726_fb_cfg_canvas(sc); 384280905Sganbold 385280905Sganbold AML_FB_LOCK_INIT(sc); 386280905Sganbold 387280905Sganbold error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE, 388280905Sganbold NULL, aml8726_fb_intr, sc, &sc->ih_cookie); 389280905Sganbold 390280905Sganbold if (error) { 391280905Sganbold device_printf(dev, "could not setup interrupt handler\n"); 392280905Sganbold goto fail; 393280905Sganbold } 394280905Sganbold 395280905Sganbold child = device_add_child(dev, "fbd", device_get_unit(dev)); 396280905Sganbold 397280905Sganbold if (!child) { 398280905Sganbold device_printf(dev, "could not add fbd\n"); 399280905Sganbold error = ENXIO; 400280905Sganbold goto fail; 401280905Sganbold } 402280905Sganbold 403280905Sganbold error = device_probe_and_attach(child); 404280905Sganbold 405280905Sganbold if (error) { 406280905Sganbold device_printf(dev, "could not attach fbd\n"); 407280905Sganbold goto fail; 408280905Sganbold } 409280905Sganbold 410280905Sganbold return (0); 411280905Sganbold 412280905Sganboldfail: 413280905Sganbold if (sc->ih_cookie) 414280905Sganbold bus_teardown_intr(dev, sc->res[3], sc->ih_cookie); 415280905Sganbold 416280905Sganbold AML_FB_LOCK_DESTROY(sc); 417280905Sganbold 418280905Sganbold bus_release_resources(dev, aml8726_fb_spec, sc->res); 419280905Sganbold 420280905Sganbold pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); 421280905Sganbold 422280905Sganbold return (error); 423280905Sganbold} 424280905Sganbold 425280905Sganboldstatic int 426280905Sganboldaml8726_fb_detach(device_t dev) 427280905Sganbold{ 428280905Sganbold struct aml8726_fb_softc *sc = device_get_softc(dev); 429280905Sganbold 430280905Sganbold bus_generic_detach(dev); 431280905Sganbold 432280905Sganbold bus_teardown_intr(dev, sc->res[3], sc->ih_cookie); 433280905Sganbold 434280905Sganbold AML_FB_LOCK_DESTROY(sc); 435280905Sganbold 436280905Sganbold bus_release_resources(dev, aml8726_fb_spec, sc->res); 437280905Sganbold 438280905Sganbold pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); 439280905Sganbold 440280905Sganbold return (0); 441280905Sganbold} 442280905Sganbold 443280905Sganboldstatic struct fb_info * 444280905Sganboldaml8726_fb_getinfo(device_t dev) 445280905Sganbold{ 446280905Sganbold struct aml8726_fb_softc *sc = device_get_softc(dev); 447280905Sganbold 448280905Sganbold return (&sc->info); 449280905Sganbold} 450280905Sganbold 451280905Sganboldstatic device_method_t aml8726_fb_methods[] = { 452280905Sganbold /* Device interface */ 453280905Sganbold DEVMETHOD(device_probe, aml8726_fb_probe), 454280905Sganbold DEVMETHOD(device_attach, aml8726_fb_attach), 455280905Sganbold DEVMETHOD(device_detach, aml8726_fb_detach), 456280905Sganbold 457280905Sganbold /* FB interface */ 458280905Sganbold DEVMETHOD(fb_getinfo, aml8726_fb_getinfo), 459280905Sganbold 460280905Sganbold DEVMETHOD_END 461280905Sganbold}; 462280905Sganbold 463280905Sganboldstatic driver_t aml8726_fb_driver = { 464280905Sganbold "fb", 465280905Sganbold aml8726_fb_methods, 466280905Sganbold sizeof(struct aml8726_fb_softc), 467280905Sganbold}; 468280905Sganbold 469280905Sganboldstatic devclass_t aml8726_fb_devclass; 470280905Sganbold 471280905SganboldDRIVER_MODULE(fb, simplebus, aml8726_fb_driver, aml8726_fb_devclass, 0, 0); 472