diff options
Diffstat (limited to 'drivers/video/sm501fb.c')
-rw-r--r-- | drivers/video/sm501fb.c | 237 |
1 files changed, 224 insertions, 13 deletions
diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index d1c8ea83b41..35370d0ecf0 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c | |||
@@ -66,6 +66,7 @@ struct sm501fb_info { | |||
66 | struct fb_info *fb[2]; /* fb info for both heads */ | 66 | struct fb_info *fb[2]; /* fb info for both heads */ |
67 | struct resource *fbmem_res; /* framebuffer resource */ | 67 | struct resource *fbmem_res; /* framebuffer resource */ |
68 | struct resource *regs_res; /* registers resource */ | 68 | struct resource *regs_res; /* registers resource */ |
69 | struct resource *regs2d_res; /* 2d registers resource */ | ||
69 | struct sm501_platdata_fb *pdata; /* our platform data */ | 70 | struct sm501_platdata_fb *pdata; /* our platform data */ |
70 | 71 | ||
71 | unsigned long pm_crt_ctrl; /* pm: crt ctrl save */ | 72 | unsigned long pm_crt_ctrl; /* pm: crt ctrl save */ |
@@ -73,6 +74,7 @@ struct sm501fb_info { | |||
73 | int irq; | 74 | int irq; |
74 | int swap_endian; /* set to swap rgb=>bgr */ | 75 | int swap_endian; /* set to swap rgb=>bgr */ |
75 | void __iomem *regs; /* remapped registers */ | 76 | void __iomem *regs; /* remapped registers */ |
77 | void __iomem *regs2d; /* 2d remapped registers */ | ||
76 | void __iomem *fbmem; /* remapped framebuffer */ | 78 | void __iomem *fbmem; /* remapped framebuffer */ |
77 | size_t fbmem_len; /* length of remapped region */ | 79 | size_t fbmem_len; /* length of remapped region */ |
78 | }; | 80 | }; |
@@ -123,9 +125,9 @@ static inline void sm501fb_sync_regs(struct sm501fb_info *info) | |||
123 | * This is an attempt to lay out memory for the two framebuffers and | 125 | * This is an attempt to lay out memory for the two framebuffers and |
124 | * everything else | 126 | * everything else |
125 | * | 127 | * |
126 | * |fbmem_res->start fbmem_res->end| | 128 | * |fbmem_res->start fbmem_res->end| |
127 | * | | | 129 | * | | |
128 | * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K | | 130 | * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K | |
129 | * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-| | 131 | * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-| |
130 | * | 132 | * |
131 | * The "spare" space is for the 2d engine data | 133 | * The "spare" space is for the 2d engine data |
@@ -1246,7 +1248,173 @@ static ssize_t sm501fb_debug_show_pnl(struct device *dev, | |||
1246 | 1248 | ||
1247 | static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL); | 1249 | static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL); |
1248 | 1250 | ||
1249 | /* framebuffer ops */ | 1251 | /* acceleration operations */ |
1252 | static int sm501fb_sync(struct fb_info *info) | ||
1253 | { | ||
1254 | int count = 1000000; | ||
1255 | struct sm501fb_par *par = info->par; | ||
1256 | struct sm501fb_info *fbi = par->info; | ||
1257 | |||
1258 | /* wait for the 2d engine to be ready */ | ||
1259 | while ((count > 0) && | ||
1260 | (readl(fbi->regs + SM501_SYSTEM_CONTROL) & | ||
1261 | SM501_SYSCTRL_2D_ENGINE_STATUS) != 0) | ||
1262 | count--; | ||
1263 | |||
1264 | if (count <= 0) { | ||
1265 | dev_err(info->dev, "Timeout waiting for 2d engine sync\n"); | ||
1266 | return 1; | ||
1267 | } | ||
1268 | return 0; | ||
1269 | } | ||
1270 | |||
1271 | static void sm501fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) | ||
1272 | { | ||
1273 | struct sm501fb_par *par = info->par; | ||
1274 | struct sm501fb_info *fbi = par->info; | ||
1275 | int width = area->width; | ||
1276 | int height = area->height; | ||
1277 | int sx = area->sx; | ||
1278 | int sy = area->sy; | ||
1279 | int dx = area->dx; | ||
1280 | int dy = area->dy; | ||
1281 | unsigned long rtl = 0; | ||
1282 | |||
1283 | /* source clip */ | ||
1284 | if ((sx >= info->var.xres_virtual) || | ||
1285 | (sy >= info->var.yres_virtual)) | ||
1286 | /* source Area not within virtual screen, skipping */ | ||
1287 | return; | ||
1288 | if ((sx + width) >= info->var.xres_virtual) | ||
1289 | width = info->var.xres_virtual - sx - 1; | ||
1290 | if ((sy + height) >= info->var.yres_virtual) | ||
1291 | height = info->var.yres_virtual - sy - 1; | ||
1292 | |||
1293 | /* dest clip */ | ||
1294 | if ((dx >= info->var.xres_virtual) || | ||
1295 | (dy >= info->var.yres_virtual)) | ||
1296 | /* Destination Area not within virtual screen, skipping */ | ||
1297 | return; | ||
1298 | if ((dx + width) >= info->var.xres_virtual) | ||
1299 | width = info->var.xres_virtual - dx - 1; | ||
1300 | if ((dy + height) >= info->var.yres_virtual) | ||
1301 | height = info->var.yres_virtual - dy - 1; | ||
1302 | |||
1303 | if ((sx < dx) || (sy < dy)) { | ||
1304 | rtl = 1 << 27; | ||
1305 | sx += width - 1; | ||
1306 | dx += width - 1; | ||
1307 | sy += height - 1; | ||
1308 | dy += height - 1; | ||
1309 | } | ||
1310 | |||
1311 | if (sm501fb_sync(info)) | ||
1312 | return; | ||
1313 | |||
1314 | /* set the base addresses */ | ||
1315 | writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE); | ||
1316 | writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_DESTINATION_BASE); | ||
1317 | |||
1318 | /* set the window width */ | ||
1319 | writel((info->var.xres << 16) | info->var.xres, | ||
1320 | fbi->regs2d + SM501_2D_WINDOW_WIDTH); | ||
1321 | |||
1322 | /* set window stride */ | ||
1323 | writel((info->var.xres_virtual << 16) | info->var.xres_virtual, | ||
1324 | fbi->regs2d + SM501_2D_PITCH); | ||
1325 | |||
1326 | /* set data format */ | ||
1327 | switch (info->var.bits_per_pixel) { | ||
1328 | case 8: | ||
1329 | writel(0, fbi->regs2d + SM501_2D_STRETCH); | ||
1330 | break; | ||
1331 | case 16: | ||
1332 | writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH); | ||
1333 | break; | ||
1334 | case 32: | ||
1335 | writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH); | ||
1336 | break; | ||
1337 | } | ||
1338 | |||
1339 | /* 2d compare mask */ | ||
1340 | writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK); | ||
1341 | |||
1342 | /* 2d mask */ | ||
1343 | writel(0xffffffff, fbi->regs2d + SM501_2D_MASK); | ||
1344 | |||
1345 | /* source and destination x y */ | ||
1346 | writel((sx << 16) | sy, fbi->regs2d + SM501_2D_SOURCE); | ||
1347 | writel((dx << 16) | dy, fbi->regs2d + SM501_2D_DESTINATION); | ||
1348 | |||
1349 | /* w/h */ | ||
1350 | writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION); | ||
1351 | |||
1352 | /* do area move */ | ||
1353 | writel(0x800000cc | rtl, fbi->regs2d + SM501_2D_CONTROL); | ||
1354 | } | ||
1355 | |||
1356 | static void sm501fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | ||
1357 | { | ||
1358 | struct sm501fb_par *par = info->par; | ||
1359 | struct sm501fb_info *fbi = par->info; | ||
1360 | int width = rect->width, height = rect->height; | ||
1361 | |||
1362 | if ((rect->dx >= info->var.xres_virtual) || | ||
1363 | (rect->dy >= info->var.yres_virtual)) | ||
1364 | /* Rectangle not within virtual screen, skipping */ | ||
1365 | return; | ||
1366 | if ((rect->dx + width) >= info->var.xres_virtual) | ||
1367 | width = info->var.xres_virtual - rect->dx - 1; | ||
1368 | if ((rect->dy + height) >= info->var.yres_virtual) | ||
1369 | height = info->var.yres_virtual - rect->dy - 1; | ||
1370 | |||
1371 | if (sm501fb_sync(info)) | ||
1372 | return; | ||
1373 | |||
1374 | /* set the base addresses */ | ||
1375 | writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE); | ||
1376 | writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_DESTINATION_BASE); | ||
1377 | |||
1378 | /* set the window width */ | ||
1379 | writel((info->var.xres << 16) | info->var.xres, | ||
1380 | fbi->regs2d + SM501_2D_WINDOW_WIDTH); | ||
1381 | |||
1382 | /* set window stride */ | ||
1383 | writel((info->var.xres_virtual << 16) | info->var.xres_virtual, | ||
1384 | fbi->regs2d + SM501_2D_PITCH); | ||
1385 | |||
1386 | /* set data format */ | ||
1387 | switch (info->var.bits_per_pixel) { | ||
1388 | case 8: | ||
1389 | writel(0, fbi->regs2d + SM501_2D_STRETCH); | ||
1390 | break; | ||
1391 | case 16: | ||
1392 | writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH); | ||
1393 | break; | ||
1394 | case 32: | ||
1395 | writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH); | ||
1396 | break; | ||
1397 | } | ||
1398 | |||
1399 | /* 2d compare mask */ | ||
1400 | writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK); | ||
1401 | |||
1402 | /* 2d mask */ | ||
1403 | writel(0xffffffff, fbi->regs2d + SM501_2D_MASK); | ||
1404 | |||
1405 | /* colour */ | ||
1406 | writel(rect->color, fbi->regs2d + SM501_2D_FOREGROUND); | ||
1407 | |||
1408 | /* x y */ | ||
1409 | writel((rect->dx << 16) | rect->dy, fbi->regs2d + SM501_2D_DESTINATION); | ||
1410 | |||
1411 | /* w/h */ | ||
1412 | writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION); | ||
1413 | |||
1414 | /* do rectangle fill */ | ||
1415 | writel(0x800100cc, fbi->regs2d + SM501_2D_CONTROL); | ||
1416 | } | ||
1417 | |||
1250 | 1418 | ||
1251 | static struct fb_ops sm501fb_ops_crt = { | 1419 | static struct fb_ops sm501fb_ops_crt = { |
1252 | .owner = THIS_MODULE, | 1420 | .owner = THIS_MODULE, |
@@ -1256,9 +1424,10 @@ static struct fb_ops sm501fb_ops_crt = { | |||
1256 | .fb_setcolreg = sm501fb_setcolreg, | 1424 | .fb_setcolreg = sm501fb_setcolreg, |
1257 | .fb_pan_display = sm501fb_pan_crt, | 1425 | .fb_pan_display = sm501fb_pan_crt, |
1258 | .fb_cursor = sm501fb_cursor, | 1426 | .fb_cursor = sm501fb_cursor, |
1259 | .fb_fillrect = cfb_fillrect, | 1427 | .fb_fillrect = sm501fb_fillrect, |
1260 | .fb_copyarea = cfb_copyarea, | 1428 | .fb_copyarea = sm501fb_copyarea, |
1261 | .fb_imageblit = cfb_imageblit, | 1429 | .fb_imageblit = cfb_imageblit, |
1430 | .fb_sync = sm501fb_sync, | ||
1262 | }; | 1431 | }; |
1263 | 1432 | ||
1264 | static struct fb_ops sm501fb_ops_pnl = { | 1433 | static struct fb_ops sm501fb_ops_pnl = { |
@@ -1269,9 +1438,10 @@ static struct fb_ops sm501fb_ops_pnl = { | |||
1269 | .fb_blank = sm501fb_blank_pnl, | 1438 | .fb_blank = sm501fb_blank_pnl, |
1270 | .fb_setcolreg = sm501fb_setcolreg, | 1439 | .fb_setcolreg = sm501fb_setcolreg, |
1271 | .fb_cursor = sm501fb_cursor, | 1440 | .fb_cursor = sm501fb_cursor, |
1272 | .fb_fillrect = cfb_fillrect, | 1441 | .fb_fillrect = sm501fb_fillrect, |
1273 | .fb_copyarea = cfb_copyarea, | 1442 | .fb_copyarea = sm501fb_copyarea, |
1274 | .fb_imageblit = cfb_imageblit, | 1443 | .fb_imageblit = cfb_imageblit, |
1444 | .fb_sync = sm501fb_sync, | ||
1275 | }; | 1445 | }; |
1276 | 1446 | ||
1277 | /* sm501_init_cursor | 1447 | /* sm501_init_cursor |
@@ -1329,7 +1499,8 @@ static int sm501fb_start(struct sm501fb_info *info, | |||
1329 | dev_warn(dev, "no irq for device\n"); | 1499 | dev_warn(dev, "no irq for device\n"); |
1330 | } | 1500 | } |
1331 | 1501 | ||
1332 | /* allocate, reserve and remap resources for registers */ | 1502 | /* allocate, reserve and remap resources for display |
1503 | * controller registers */ | ||
1333 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1504 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1334 | if (res == NULL) { | 1505 | if (res == NULL) { |
1335 | dev_err(dev, "no resource definition for registers\n"); | 1506 | dev_err(dev, "no resource definition for registers\n"); |
@@ -1354,12 +1525,38 @@ static int sm501fb_start(struct sm501fb_info *info, | |||
1354 | goto err_regs_res; | 1525 | goto err_regs_res; |
1355 | } | 1526 | } |
1356 | 1527 | ||
1528 | /* allocate, reserve and remap resources for 2d | ||
1529 | * controller registers */ | ||
1530 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
1531 | if (res == NULL) { | ||
1532 | dev_err(dev, "no resource definition for 2d registers\n"); | ||
1533 | ret = -ENOENT; | ||
1534 | goto err_regs_map; | ||
1535 | } | ||
1536 | |||
1537 | info->regs2d_res = request_mem_region(res->start, | ||
1538 | resource_size(res), | ||
1539 | pdev->name); | ||
1540 | |||
1541 | if (info->regs2d_res == NULL) { | ||
1542 | dev_err(dev, "cannot claim registers\n"); | ||
1543 | ret = -ENXIO; | ||
1544 | goto err_regs_map; | ||
1545 | } | ||
1546 | |||
1547 | info->regs2d = ioremap(res->start, resource_size(res)); | ||
1548 | if (info->regs2d == NULL) { | ||
1549 | dev_err(dev, "cannot remap registers\n"); | ||
1550 | ret = -ENXIO; | ||
1551 | goto err_regs2d_res; | ||
1552 | } | ||
1553 | |||
1357 | /* allocate, reserve resources for framebuffer */ | 1554 | /* allocate, reserve resources for framebuffer */ |
1358 | res = platform_get_resource(pdev, IORESOURCE_MEM, 2); | 1555 | res = platform_get_resource(pdev, IORESOURCE_MEM, 2); |
1359 | if (res == NULL) { | 1556 | if (res == NULL) { |
1360 | dev_err(dev, "no memory resource defined\n"); | 1557 | dev_err(dev, "no memory resource defined\n"); |
1361 | ret = -ENXIO; | 1558 | ret = -ENXIO; |
1362 | goto err_regs_map; | 1559 | goto err_regs2d_map; |
1363 | } | 1560 | } |
1364 | 1561 | ||
1365 | info->fbmem_res = request_mem_region(res->start, | 1562 | info->fbmem_res = request_mem_region(res->start, |
@@ -1368,7 +1565,7 @@ static int sm501fb_start(struct sm501fb_info *info, | |||
1368 | if (info->fbmem_res == NULL) { | 1565 | if (info->fbmem_res == NULL) { |
1369 | dev_err(dev, "cannot claim framebuffer\n"); | 1566 | dev_err(dev, "cannot claim framebuffer\n"); |
1370 | ret = -ENXIO; | 1567 | ret = -ENXIO; |
1371 | goto err_regs_map; | 1568 | goto err_regs2d_map; |
1372 | } | 1569 | } |
1373 | 1570 | ||
1374 | info->fbmem = ioremap(res->start, resource_size(res)); | 1571 | info->fbmem = ioremap(res->start, resource_size(res)); |
@@ -1389,8 +1586,10 @@ static int sm501fb_start(struct sm501fb_info *info, | |||
1389 | /* enable display controller */ | 1586 | /* enable display controller */ |
1390 | sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1); | 1587 | sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1); |
1391 | 1588 | ||
1392 | /* setup cursors */ | 1589 | /* enable 2d controller */ |
1590 | sm501_unit_power(dev->parent, SM501_GATE_2D_ENGINE, 1); | ||
1393 | 1591 | ||
1592 | /* setup cursors */ | ||
1394 | sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR); | 1593 | sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR); |
1395 | sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR); | 1594 | sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR); |
1396 | 1595 | ||
@@ -1400,6 +1599,13 @@ static int sm501fb_start(struct sm501fb_info *info, | |||
1400 | release_resource(info->fbmem_res); | 1599 | release_resource(info->fbmem_res); |
1401 | kfree(info->fbmem_res); | 1600 | kfree(info->fbmem_res); |
1402 | 1601 | ||
1602 | err_regs2d_map: | ||
1603 | iounmap(info->regs2d); | ||
1604 | |||
1605 | err_regs2d_res: | ||
1606 | release_resource(info->regs2d_res); | ||
1607 | kfree(info->regs2d_res); | ||
1608 | |||
1403 | err_regs_map: | 1609 | err_regs_map: |
1404 | iounmap(info->regs); | 1610 | iounmap(info->regs); |
1405 | 1611 | ||
@@ -1420,6 +1626,10 @@ static void sm501fb_stop(struct sm501fb_info *info) | |||
1420 | release_resource(info->fbmem_res); | 1626 | release_resource(info->fbmem_res); |
1421 | kfree(info->fbmem_res); | 1627 | kfree(info->fbmem_res); |
1422 | 1628 | ||
1629 | iounmap(info->regs2d); | ||
1630 | release_resource(info->regs2d_res); | ||
1631 | kfree(info->regs2d_res); | ||
1632 | |||
1423 | iounmap(info->regs); | 1633 | iounmap(info->regs); |
1424 | release_resource(info->regs_res); | 1634 | release_resource(info->regs_res); |
1425 | kfree(info->regs_res); | 1635 | kfree(info->regs_res); |
@@ -1486,7 +1696,8 @@ static int sm501fb_init_fb(struct fb_info *fb, | |||
1486 | par->ops.fb_cursor = NULL; | 1696 | par->ops.fb_cursor = NULL; |
1487 | 1697 | ||
1488 | fb->fbops = &par->ops; | 1698 | fb->fbops = &par->ops; |
1489 | fb->flags = FBINFO_FLAG_DEFAULT | | 1699 | fb->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST | |
1700 | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | | ||
1490 | FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; | 1701 | FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; |
1491 | 1702 | ||
1492 | /* fixed data */ | 1703 | /* fixed data */ |