mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 11:18:45 +07:00
0adcdbcb17
framebuffer_alloc() can fail only on kzalloc() memory allocation failure and since kzalloc() will print error message in such case we can omit printing extra error message in drivers (which BTW is what the majority of framebuffer_alloc() users is doing already). Cc: "Bruno Prémont" <bonbons@linux-vserver.org> Cc: Jiri Kosina <jikos@kernel.org> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
691 lines
16 KiB
C
691 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
|
|
* JZ4740 SoC LCD framebuffer driver
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/console.h>
|
|
#include <linux/fb.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <asm/mach-jz4740/jz4740_fb.h>
|
|
|
|
#define JZ_REG_LCD_CFG 0x00
|
|
#define JZ_REG_LCD_VSYNC 0x04
|
|
#define JZ_REG_LCD_HSYNC 0x08
|
|
#define JZ_REG_LCD_VAT 0x0C
|
|
#define JZ_REG_LCD_DAH 0x10
|
|
#define JZ_REG_LCD_DAV 0x14
|
|
#define JZ_REG_LCD_PS 0x18
|
|
#define JZ_REG_LCD_CLS 0x1C
|
|
#define JZ_REG_LCD_SPL 0x20
|
|
#define JZ_REG_LCD_REV 0x24
|
|
#define JZ_REG_LCD_CTRL 0x30
|
|
#define JZ_REG_LCD_STATE 0x34
|
|
#define JZ_REG_LCD_IID 0x38
|
|
#define JZ_REG_LCD_DA0 0x40
|
|
#define JZ_REG_LCD_SA0 0x44
|
|
#define JZ_REG_LCD_FID0 0x48
|
|
#define JZ_REG_LCD_CMD0 0x4C
|
|
#define JZ_REG_LCD_DA1 0x50
|
|
#define JZ_REG_LCD_SA1 0x54
|
|
#define JZ_REG_LCD_FID1 0x58
|
|
#define JZ_REG_LCD_CMD1 0x5C
|
|
|
|
#define JZ_LCD_CFG_SLCD BIT(31)
|
|
#define JZ_LCD_CFG_PS_DISABLE BIT(23)
|
|
#define JZ_LCD_CFG_CLS_DISABLE BIT(22)
|
|
#define JZ_LCD_CFG_SPL_DISABLE BIT(21)
|
|
#define JZ_LCD_CFG_REV_DISABLE BIT(20)
|
|
#define JZ_LCD_CFG_HSYNCM BIT(19)
|
|
#define JZ_LCD_CFG_PCLKM BIT(18)
|
|
#define JZ_LCD_CFG_INV BIT(17)
|
|
#define JZ_LCD_CFG_SYNC_DIR BIT(16)
|
|
#define JZ_LCD_CFG_PS_POLARITY BIT(15)
|
|
#define JZ_LCD_CFG_CLS_POLARITY BIT(14)
|
|
#define JZ_LCD_CFG_SPL_POLARITY BIT(13)
|
|
#define JZ_LCD_CFG_REV_POLARITY BIT(12)
|
|
#define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11)
|
|
#define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10)
|
|
#define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9)
|
|
#define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8)
|
|
#define JZ_LCD_CFG_18_BIT BIT(7)
|
|
#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4))
|
|
#define JZ_LCD_CFG_MODE_MASK 0xf
|
|
|
|
#define JZ_LCD_CTRL_BURST_4 (0x0 << 28)
|
|
#define JZ_LCD_CTRL_BURST_8 (0x1 << 28)
|
|
#define JZ_LCD_CTRL_BURST_16 (0x2 << 28)
|
|
#define JZ_LCD_CTRL_RGB555 BIT(27)
|
|
#define JZ_LCD_CTRL_OFUP BIT(26)
|
|
#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24)
|
|
#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24)
|
|
#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24)
|
|
#define JZ_LCD_CTRL_PDD_MASK (0xff << 16)
|
|
#define JZ_LCD_CTRL_EOF_IRQ BIT(13)
|
|
#define JZ_LCD_CTRL_SOF_IRQ BIT(12)
|
|
#define JZ_LCD_CTRL_OFU_IRQ BIT(11)
|
|
#define JZ_LCD_CTRL_IFU0_IRQ BIT(10)
|
|
#define JZ_LCD_CTRL_IFU1_IRQ BIT(9)
|
|
#define JZ_LCD_CTRL_DD_IRQ BIT(8)
|
|
#define JZ_LCD_CTRL_QDD_IRQ BIT(7)
|
|
#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6)
|
|
#define JZ_LCD_CTRL_LSB_FISRT BIT(5)
|
|
#define JZ_LCD_CTRL_DISABLE BIT(4)
|
|
#define JZ_LCD_CTRL_ENABLE BIT(3)
|
|
#define JZ_LCD_CTRL_BPP_1 0x0
|
|
#define JZ_LCD_CTRL_BPP_2 0x1
|
|
#define JZ_LCD_CTRL_BPP_4 0x2
|
|
#define JZ_LCD_CTRL_BPP_8 0x3
|
|
#define JZ_LCD_CTRL_BPP_15_16 0x4
|
|
#define JZ_LCD_CTRL_BPP_18_24 0x5
|
|
|
|
#define JZ_LCD_CMD_SOF_IRQ BIT(31)
|
|
#define JZ_LCD_CMD_EOF_IRQ BIT(30)
|
|
#define JZ_LCD_CMD_ENABLE_PAL BIT(28)
|
|
|
|
#define JZ_LCD_SYNC_MASK 0x3ff
|
|
|
|
#define JZ_LCD_STATE_DISABLED BIT(0)
|
|
|
|
struct jzfb_framedesc {
|
|
uint32_t next;
|
|
uint32_t addr;
|
|
uint32_t id;
|
|
uint32_t cmd;
|
|
} __packed;
|
|
|
|
struct jzfb {
|
|
struct fb_info *fb;
|
|
struct platform_device *pdev;
|
|
void __iomem *base;
|
|
struct resource *mem;
|
|
struct jz4740_fb_platform_data *pdata;
|
|
|
|
size_t vidmem_size;
|
|
void *vidmem;
|
|
dma_addr_t vidmem_phys;
|
|
struct jzfb_framedesc *framedesc;
|
|
dma_addr_t framedesc_phys;
|
|
|
|
struct clk *ldclk;
|
|
struct clk *lpclk;
|
|
|
|
unsigned is_enabled:1;
|
|
struct mutex lock;
|
|
|
|
uint32_t pseudo_palette[16];
|
|
};
|
|
|
|
static const struct fb_fix_screeninfo jzfb_fix = {
|
|
.id = "JZ4740 FB",
|
|
.type = FB_TYPE_PACKED_PIXELS,
|
|
.visual = FB_VISUAL_TRUECOLOR,
|
|
.xpanstep = 0,
|
|
.ypanstep = 0,
|
|
.ywrapstep = 0,
|
|
.accel = FB_ACCEL_NONE,
|
|
};
|
|
|
|
/* Based on CNVT_TOHW macro from skeletonfb.c */
|
|
static inline uint32_t jzfb_convert_color_to_hw(unsigned val,
|
|
struct fb_bitfield *bf)
|
|
{
|
|
return (((val << bf->length) + 0x7FFF - val) >> 16) << bf->offset;
|
|
}
|
|
|
|
static int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green,
|
|
unsigned blue, unsigned transp, struct fb_info *fb)
|
|
{
|
|
uint32_t color;
|
|
|
|
if (regno >= 16)
|
|
return -EINVAL;
|
|
|
|
color = jzfb_convert_color_to_hw(red, &fb->var.red);
|
|
color |= jzfb_convert_color_to_hw(green, &fb->var.green);
|
|
color |= jzfb_convert_color_to_hw(blue, &fb->var.blue);
|
|
color |= jzfb_convert_color_to_hw(transp, &fb->var.transp);
|
|
|
|
((uint32_t *)(fb->pseudo_palette))[regno] = color;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int jzfb_get_controller_bpp(struct jzfb *jzfb)
|
|
{
|
|
switch (jzfb->pdata->bpp) {
|
|
case 18:
|
|
case 24:
|
|
return 32;
|
|
case 15:
|
|
return 16;
|
|
default:
|
|
return jzfb->pdata->bpp;
|
|
}
|
|
}
|
|
|
|
static struct fb_videomode *jzfb_get_mode(struct jzfb *jzfb,
|
|
struct fb_var_screeninfo *var)
|
|
{
|
|
size_t i;
|
|
struct fb_videomode *mode = jzfb->pdata->modes;
|
|
|
|
for (i = 0; i < jzfb->pdata->num_modes; ++i, ++mode) {
|
|
if (mode->xres == var->xres && mode->yres == var->yres)
|
|
return mode;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
|
|
{
|
|
struct jzfb *jzfb = fb->par;
|
|
struct fb_videomode *mode;
|
|
|
|
if (var->bits_per_pixel != jzfb_get_controller_bpp(jzfb) &&
|
|
var->bits_per_pixel != jzfb->pdata->bpp)
|
|
return -EINVAL;
|
|
|
|
mode = jzfb_get_mode(jzfb, var);
|
|
if (mode == NULL)
|
|
return -EINVAL;
|
|
|
|
fb_videomode_to_var(var, mode);
|
|
|
|
switch (jzfb->pdata->bpp) {
|
|
case 8:
|
|
break;
|
|
case 15:
|
|
var->red.offset = 10;
|
|
var->red.length = 5;
|
|
var->green.offset = 6;
|
|
var->green.length = 5;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 5;
|
|
break;
|
|
case 16:
|
|
var->red.offset = 11;
|
|
var->red.length = 5;
|
|
var->green.offset = 5;
|
|
var->green.length = 6;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 5;
|
|
break;
|
|
case 18:
|
|
var->red.offset = 16;
|
|
var->red.length = 6;
|
|
var->green.offset = 8;
|
|
var->green.length = 6;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 6;
|
|
var->bits_per_pixel = 32;
|
|
break;
|
|
case 32:
|
|
case 24:
|
|
var->transp.offset = 24;
|
|
var->transp.length = 8;
|
|
var->red.offset = 16;
|
|
var->red.length = 8;
|
|
var->green.offset = 8;
|
|
var->green.length = 8;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 8;
|
|
var->bits_per_pixel = 32;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int jzfb_set_par(struct fb_info *info)
|
|
{
|
|
struct jzfb *jzfb = info->par;
|
|
struct jz4740_fb_platform_data *pdata = jzfb->pdata;
|
|
struct fb_var_screeninfo *var = &info->var;
|
|
struct fb_videomode *mode;
|
|
uint16_t hds, vds;
|
|
uint16_t hde, vde;
|
|
uint16_t ht, vt;
|
|
uint32_t ctrl;
|
|
uint32_t cfg;
|
|
unsigned long rate;
|
|
|
|
mode = jzfb_get_mode(jzfb, var);
|
|
if (mode == NULL)
|
|
return -EINVAL;
|
|
|
|
if (mode == info->mode)
|
|
return 0;
|
|
|
|
info->mode = mode;
|
|
|
|
hds = mode->hsync_len + mode->left_margin;
|
|
hde = hds + mode->xres;
|
|
ht = hde + mode->right_margin;
|
|
|
|
vds = mode->vsync_len + mode->upper_margin;
|
|
vde = vds + mode->yres;
|
|
vt = vde + mode->lower_margin;
|
|
|
|
ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16;
|
|
|
|
switch (pdata->bpp) {
|
|
case 1:
|
|
ctrl |= JZ_LCD_CTRL_BPP_1;
|
|
break;
|
|
case 2:
|
|
ctrl |= JZ_LCD_CTRL_BPP_2;
|
|
break;
|
|
case 4:
|
|
ctrl |= JZ_LCD_CTRL_BPP_4;
|
|
break;
|
|
case 8:
|
|
ctrl |= JZ_LCD_CTRL_BPP_8;
|
|
break;
|
|
case 15:
|
|
ctrl |= JZ_LCD_CTRL_RGB555; /* Falltrough */
|
|
case 16:
|
|
ctrl |= JZ_LCD_CTRL_BPP_15_16;
|
|
break;
|
|
case 18:
|
|
case 24:
|
|
case 32:
|
|
ctrl |= JZ_LCD_CTRL_BPP_18_24;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
cfg = pdata->lcd_type & 0xf;
|
|
|
|
if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
|
|
cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
|
|
|
|
if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
|
|
cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW;
|
|
|
|
if (pdata->pixclk_falling_edge)
|
|
cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE;
|
|
|
|
if (pdata->date_enable_active_low)
|
|
cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW;
|
|
|
|
if (pdata->lcd_type == JZ_LCD_TYPE_GENERIC_18_BIT)
|
|
cfg |= JZ_LCD_CFG_18_BIT;
|
|
|
|
if (mode->pixclock) {
|
|
rate = PICOS2KHZ(mode->pixclock) * 1000;
|
|
mode->refresh = rate / vt / ht;
|
|
} else {
|
|
if (pdata->lcd_type == JZ_LCD_TYPE_8BIT_SERIAL)
|
|
rate = mode->refresh * (vt + 2 * mode->xres) * ht;
|
|
else
|
|
rate = mode->refresh * vt * ht;
|
|
|
|
mode->pixclock = KHZ2PICOS(rate / 1000);
|
|
}
|
|
|
|
mutex_lock(&jzfb->lock);
|
|
if (!jzfb->is_enabled)
|
|
clk_enable(jzfb->ldclk);
|
|
else
|
|
ctrl |= JZ_LCD_CTRL_ENABLE;
|
|
|
|
switch (pdata->lcd_type) {
|
|
case JZ_LCD_TYPE_SPECIAL_TFT_1:
|
|
case JZ_LCD_TYPE_SPECIAL_TFT_2:
|
|
case JZ_LCD_TYPE_SPECIAL_TFT_3:
|
|
writel(pdata->special_tft_config.spl, jzfb->base + JZ_REG_LCD_SPL);
|
|
writel(pdata->special_tft_config.cls, jzfb->base + JZ_REG_LCD_CLS);
|
|
writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_PS);
|
|
writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_REV);
|
|
break;
|
|
default:
|
|
cfg |= JZ_LCD_CFG_PS_DISABLE;
|
|
cfg |= JZ_LCD_CFG_CLS_DISABLE;
|
|
cfg |= JZ_LCD_CFG_SPL_DISABLE;
|
|
cfg |= JZ_LCD_CFG_REV_DISABLE;
|
|
break;
|
|
}
|
|
|
|
writel(mode->hsync_len, jzfb->base + JZ_REG_LCD_HSYNC);
|
|
writel(mode->vsync_len, jzfb->base + JZ_REG_LCD_VSYNC);
|
|
|
|
writel((ht << 16) | vt, jzfb->base + JZ_REG_LCD_VAT);
|
|
|
|
writel((hds << 16) | hde, jzfb->base + JZ_REG_LCD_DAH);
|
|
writel((vds << 16) | vde, jzfb->base + JZ_REG_LCD_DAV);
|
|
|
|
writel(cfg, jzfb->base + JZ_REG_LCD_CFG);
|
|
|
|
writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
|
|
|
|
if (!jzfb->is_enabled)
|
|
clk_disable_unprepare(jzfb->ldclk);
|
|
|
|
mutex_unlock(&jzfb->lock);
|
|
|
|
clk_set_rate(jzfb->lpclk, rate);
|
|
clk_set_rate(jzfb->ldclk, rate * 3);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void jzfb_enable(struct jzfb *jzfb)
|
|
{
|
|
uint32_t ctrl;
|
|
|
|
clk_prepare_enable(jzfb->ldclk);
|
|
|
|
pinctrl_pm_select_default_state(&jzfb->pdev->dev);
|
|
|
|
writel(0, jzfb->base + JZ_REG_LCD_STATE);
|
|
|
|
writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0);
|
|
|
|
ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
|
|
ctrl |= JZ_LCD_CTRL_ENABLE;
|
|
ctrl &= ~JZ_LCD_CTRL_DISABLE;
|
|
writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
|
|
}
|
|
|
|
static void jzfb_disable(struct jzfb *jzfb)
|
|
{
|
|
uint32_t ctrl;
|
|
|
|
ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL);
|
|
ctrl |= JZ_LCD_CTRL_DISABLE;
|
|
writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL);
|
|
do {
|
|
ctrl = readl(jzfb->base + JZ_REG_LCD_STATE);
|
|
} while (!(ctrl & JZ_LCD_STATE_DISABLED));
|
|
|
|
pinctrl_pm_select_sleep_state(&jzfb->pdev->dev);
|
|
|
|
clk_disable_unprepare(jzfb->ldclk);
|
|
}
|
|
|
|
static int jzfb_blank(int blank_mode, struct fb_info *info)
|
|
{
|
|
struct jzfb *jzfb = info->par;
|
|
|
|
switch (blank_mode) {
|
|
case FB_BLANK_UNBLANK:
|
|
mutex_lock(&jzfb->lock);
|
|
if (jzfb->is_enabled) {
|
|
mutex_unlock(&jzfb->lock);
|
|
return 0;
|
|
}
|
|
|
|
jzfb_enable(jzfb);
|
|
jzfb->is_enabled = 1;
|
|
|
|
mutex_unlock(&jzfb->lock);
|
|
break;
|
|
default:
|
|
mutex_lock(&jzfb->lock);
|
|
if (!jzfb->is_enabled) {
|
|
mutex_unlock(&jzfb->lock);
|
|
return 0;
|
|
}
|
|
|
|
jzfb_disable(jzfb);
|
|
jzfb->is_enabled = 0;
|
|
|
|
mutex_unlock(&jzfb->lock);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int jzfb_alloc_devmem(struct jzfb *jzfb)
|
|
{
|
|
int max_videosize = 0;
|
|
struct fb_videomode *mode = jzfb->pdata->modes;
|
|
int i;
|
|
|
|
for (i = 0; i < jzfb->pdata->num_modes; ++mode, ++i) {
|
|
if (max_videosize < mode->xres * mode->yres)
|
|
max_videosize = mode->xres * mode->yres;
|
|
}
|
|
|
|
max_videosize *= jzfb_get_controller_bpp(jzfb) >> 3;
|
|
|
|
jzfb->framedesc = dma_alloc_coherent(&jzfb->pdev->dev,
|
|
sizeof(*jzfb->framedesc),
|
|
&jzfb->framedesc_phys, GFP_KERNEL);
|
|
|
|
if (!jzfb->framedesc)
|
|
return -ENOMEM;
|
|
|
|
jzfb->vidmem_size = PAGE_ALIGN(max_videosize);
|
|
jzfb->vidmem = dma_alloc_coherent(&jzfb->pdev->dev,
|
|
jzfb->vidmem_size,
|
|
&jzfb->vidmem_phys, GFP_KERNEL);
|
|
|
|
if (!jzfb->vidmem)
|
|
goto err_free_framedesc;
|
|
|
|
jzfb->framedesc->next = jzfb->framedesc_phys;
|
|
jzfb->framedesc->addr = jzfb->vidmem_phys;
|
|
jzfb->framedesc->id = 0xdeafbead;
|
|
jzfb->framedesc->cmd = 0;
|
|
jzfb->framedesc->cmd |= max_videosize / 4;
|
|
|
|
return 0;
|
|
|
|
err_free_framedesc:
|
|
dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc),
|
|
jzfb->framedesc, jzfb->framedesc_phys);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static void jzfb_free_devmem(struct jzfb *jzfb)
|
|
{
|
|
dma_free_coherent(&jzfb->pdev->dev, jzfb->vidmem_size,
|
|
jzfb->vidmem, jzfb->vidmem_phys);
|
|
dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc),
|
|
jzfb->framedesc, jzfb->framedesc_phys);
|
|
}
|
|
|
|
static struct fb_ops jzfb_ops = {
|
|
.owner = THIS_MODULE,
|
|
.fb_check_var = jzfb_check_var,
|
|
.fb_set_par = jzfb_set_par,
|
|
.fb_blank = jzfb_blank,
|
|
.fb_fillrect = sys_fillrect,
|
|
.fb_copyarea = sys_copyarea,
|
|
.fb_imageblit = sys_imageblit,
|
|
.fb_setcolreg = jzfb_setcolreg,
|
|
};
|
|
|
|
static int jzfb_probe(struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
struct jzfb *jzfb;
|
|
struct fb_info *fb;
|
|
struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data;
|
|
struct resource *mem;
|
|
|
|
if (!pdata) {
|
|
dev_err(&pdev->dev, "Missing platform data\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev);
|
|
if (!fb)
|
|
return -ENOMEM;
|
|
|
|
fb->fbops = &jzfb_ops;
|
|
fb->flags = FBINFO_DEFAULT;
|
|
|
|
jzfb = fb->par;
|
|
jzfb->pdev = pdev;
|
|
jzfb->pdata = pdata;
|
|
|
|
jzfb->ldclk = devm_clk_get(&pdev->dev, "lcd");
|
|
if (IS_ERR(jzfb->ldclk)) {
|
|
ret = PTR_ERR(jzfb->ldclk);
|
|
dev_err(&pdev->dev, "Failed to get lcd clock: %d\n", ret);
|
|
goto err_framebuffer_release;
|
|
}
|
|
|
|
jzfb->lpclk = devm_clk_get(&pdev->dev, "lcd_pclk");
|
|
if (IS_ERR(jzfb->lpclk)) {
|
|
ret = PTR_ERR(jzfb->lpclk);
|
|
dev_err(&pdev->dev, "Failed to get lcd pixel clock: %d\n", ret);
|
|
goto err_framebuffer_release;
|
|
}
|
|
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
jzfb->base = devm_ioremap_resource(&pdev->dev, mem);
|
|
if (IS_ERR(jzfb->base)) {
|
|
ret = PTR_ERR(jzfb->base);
|
|
goto err_framebuffer_release;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, jzfb);
|
|
|
|
mutex_init(&jzfb->lock);
|
|
|
|
fb_videomode_to_modelist(pdata->modes, pdata->num_modes,
|
|
&fb->modelist);
|
|
fb_videomode_to_var(&fb->var, pdata->modes);
|
|
fb->var.bits_per_pixel = pdata->bpp;
|
|
jzfb_check_var(&fb->var, fb);
|
|
|
|
ret = jzfb_alloc_devmem(jzfb);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Failed to allocate video memory\n");
|
|
goto err_framebuffer_release;
|
|
}
|
|
|
|
fb->fix = jzfb_fix;
|
|
fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8;
|
|
fb->fix.mmio_start = mem->start;
|
|
fb->fix.mmio_len = resource_size(mem);
|
|
fb->fix.smem_start = jzfb->vidmem_phys;
|
|
fb->fix.smem_len = fb->fix.line_length * fb->var.yres;
|
|
fb->screen_base = jzfb->vidmem;
|
|
fb->pseudo_palette = jzfb->pseudo_palette;
|
|
|
|
fb_alloc_cmap(&fb->cmap, 256, 0);
|
|
|
|
clk_prepare_enable(jzfb->ldclk);
|
|
jzfb->is_enabled = 1;
|
|
|
|
writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0);
|
|
|
|
fb->mode = NULL;
|
|
jzfb_set_par(fb);
|
|
|
|
ret = register_framebuffer(fb);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret);
|
|
goto err_free_devmem;
|
|
}
|
|
|
|
jzfb->fb = fb;
|
|
|
|
return 0;
|
|
|
|
err_free_devmem:
|
|
fb_dealloc_cmap(&fb->cmap);
|
|
jzfb_free_devmem(jzfb);
|
|
err_framebuffer_release:
|
|
framebuffer_release(fb);
|
|
return ret;
|
|
}
|
|
|
|
static int jzfb_remove(struct platform_device *pdev)
|
|
{
|
|
struct jzfb *jzfb = platform_get_drvdata(pdev);
|
|
|
|
jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb);
|
|
|
|
fb_dealloc_cmap(&jzfb->fb->cmap);
|
|
jzfb_free_devmem(jzfb);
|
|
|
|
framebuffer_release(jzfb->fb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
static int jzfb_suspend(struct device *dev)
|
|
{
|
|
struct jzfb *jzfb = dev_get_drvdata(dev);
|
|
|
|
console_lock();
|
|
fb_set_suspend(jzfb->fb, 1);
|
|
console_unlock();
|
|
|
|
mutex_lock(&jzfb->lock);
|
|
if (jzfb->is_enabled)
|
|
jzfb_disable(jzfb);
|
|
mutex_unlock(&jzfb->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int jzfb_resume(struct device *dev)
|
|
{
|
|
struct jzfb *jzfb = dev_get_drvdata(dev);
|
|
clk_prepare_enable(jzfb->ldclk);
|
|
|
|
mutex_lock(&jzfb->lock);
|
|
if (jzfb->is_enabled)
|
|
jzfb_enable(jzfb);
|
|
mutex_unlock(&jzfb->lock);
|
|
|
|
console_lock();
|
|
fb_set_suspend(jzfb->fb, 0);
|
|
console_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops jzfb_pm_ops = {
|
|
.suspend = jzfb_suspend,
|
|
.resume = jzfb_resume,
|
|
.poweroff = jzfb_suspend,
|
|
.restore = jzfb_resume,
|
|
};
|
|
|
|
#define JZFB_PM_OPS (&jzfb_pm_ops)
|
|
|
|
#else
|
|
#define JZFB_PM_OPS NULL
|
|
#endif
|
|
|
|
static struct platform_driver jzfb_driver = {
|
|
.probe = jzfb_probe,
|
|
.remove = jzfb_remove,
|
|
.driver = {
|
|
.name = "jz4740-fb",
|
|
.pm = JZFB_PM_OPS,
|
|
},
|
|
};
|
|
module_platform_driver(jzfb_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
|
MODULE_DESCRIPTION("JZ4740 SoC LCD framebuffer driver");
|
|
MODULE_ALIAS("platform:jz4740-fb");
|