/* This driver will export a number of framebuffer interfaces depending *ontheconfigurationpassedinviatheplatformdata.Eachfbinstance *mapstoahardwarewindow.Currentlythereisnosupportforruntime *settingofthealpha-blendingfunctionsthateachwindowhas,soonly *window0isactuallyuseful. * *Window0istreatedspecially,itisusedforthebasisoftheLCD *outputtimingsandasthecontrolfortheoutputpower-downstate.
*/
/* note, the previous use of <mach/regs-fb.h> to get platform specific data *hasbeenreplacedbyusingtheplatformdevicenametopickthecorrect *configurationdataforthesystem.
*/
/* OSD can be set up if osd_size_off != 0 for this window */ if (win->variant.osd_size_off)
writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant)
+ win->variant.osd_size_off);
}
/**
* s3c_fb_enable() - Set the state of the main LCD output
* @sfb: The main framebuffer state.
* @enable: The state to set.
*/
static void s3c_fb_enable(struct s3c_fb *sfb, int enable)
{
u32 vidcon0 = readl(sfb->regs + VIDCON0);
if (enable && !sfb->output_on)
pm_runtime_get_sync(sfb->dev);
if (enable) {
vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F;
} else {
/* see the note in the framebuffer datasheet about
* why you cannot take both of these bits down at the
* same time. */
/* Enable DMA channel for this window */
if (sfb->variant.has_shadowcon) {
data = readl(sfb->regs + SHADOWCON);
data |= SHADOWCON_CHx_ENABLE(win_no);
writel(data, sfb->regs + SHADOWCON);
}
data = WINCONx_ENWIN;
sfb->enabled |= (1 << win->index);
/* note, since we have to round up the bits-per-pixel, we end up
* relying on the bitfield information for r/g/b/a to work out
* exactly which mode of operation is intended. */
switch (var->bits_per_pixel) {
case 1:
data |= WINCON0_BPPMODE_1BPP;
data |= WINCONx_BITSWP;
data |= WINCONx_BURSTLEN_4WORD;
break;
case 2:
data |= WINCON0_BPPMODE_2BPP;
data |= WINCONx_BITSWP;
data |= WINCONx_BURSTLEN_8WORD;
break;
case 4:
data |= WINCON0_BPPMODE_4BPP;
data |= WINCONx_BITSWP;
data |= WINCONx_BURSTLEN_8WORD;
break;
case 8:
if (var->transp.length != 0)
data |= WINCON1_BPPMODE_8BPP_1232;
else
data |= WINCON0_BPPMODE_8BPP_PALETTE;
data |= WINCONx_BURSTLEN_8WORD;
data |= WINCONx_BYTSWP;
break;
case 16:
if (var->transp.length != 0)
data |= WINCON1_BPPMODE_16BPP_A1555;
else
data |= WINCON0_BPPMODE_16BPP_565;
data |= WINCONx_HAWSWP;
data |= WINCONx_BURSTLEN_16WORD;
break;
case 24:
case 32:
if (var->red.length == 6) {
if (var->transp.length != 0)
data |= WINCON1_BPPMODE_19BPP_A1666;
else
data |= WINCON1_BPPMODE_18BPP_666;
} else if (var->transp.length == 1)
data |= WINCON1_BPPMODE_25BPP_A1888
| WINCON1_BLD_PIX;
else if ((var->transp.length == 4) ||
(var->transp.length == 8))
data |= WINCON1_BPPMODE_28BPP_A4888
| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
else
data |= WINCON0_BPPMODE_24BPP_888;
data |= WINCONx_WSWP;
data |= WINCONx_BURSTLEN_16WORD;
break;
}
/* Enable the colour keying for the window below this one */
if (win_no > 0) {
u32 keycon0_data = 0, keycon1_data = 0;
void __iomem *keycon = regs + sfb->variant.keycon;
/* Set alpha value width */
if (sfb->variant.has_blendcon) {
data = readl(sfb->regs + BLENDCON);
data &= ~BLENDCON_NEW_MASK;
if (var->transp.length > 4)
data |= BLENDCON_NEW_8BIT_ALPHA_VALUE;
else
data |= BLENDCON_NEW_4BIT_ALPHA_VALUE;
writel(data, sfb->regs + BLENDCON);
}
shadow_protect_win(win, 0);
pm_runtime_put_sync(sfb->dev);
return 0;
}
/**
* s3c_fb_update_palette() - set or schedule a palette update.
* @sfb: The hardware information.
* @win: The window being updated.
* @reg: The palette index being changed.
* @value: The computed palette value.
*
* Change the value of a palette register, either by directly writing to
* the palette (this requires the palette RAM to be disconnected from the
* hardware whilst this is in progress) or schedule the update for later.
*
* At the moment, since we have no VSYNC interrupt support, we simply set
* the palette entry directly.
*/
static void s3c_fb_update_palette(struct s3c_fb *sfb,
struct s3c_fb_win *win,
unsigned int reg,
u32 value)
{
void __iomem *palreg;
u32 palcon;
/**
* s3c_fb_setcolreg() - framebuffer layer request to change palette.
* @regno: The palette index to change.
* @red: The red field for the palette data.
* @green: The green field for the palette data.
* @blue: The blue field for the palette data.
* @transp: The transparency (alpha) field for the palette data.
* @info: The framebuffer being changed.
*/
static int s3c_fb_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
struct s3c_fb_win *win = info->par;
struct s3c_fb *sfb = win->parent;
unsigned int val;
switch (info->fix.visual) {
case FB_VISUAL_TRUECOLOR:
/* true-colour, use pseudo-palette */
if (regno < 16) {
u32 *pal = info->pseudo_palette;
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue);
pal[regno] = val;
}
break;
case FB_VISUAL_PSEUDOCOLOR:
if (regno < win->variant.palette_sz) {
val = chan_to_field(red, &win->palette.r);
val |= chan_to_field(green, &win->palette.g);
val |= chan_to_field(blue, &win->palette.b);
s3c_fb_update_palette(sfb, win, regno, val);
}
break;
default:
pm_runtime_put_sync(sfb->dev);
return 1; /* unknown type */
}
pm_runtime_put_sync(sfb->dev);
return 0;
}
/**
* s3c_fb_blank() - blank or unblank the given window
* @blank_mode: The blank state from FB_BLANK_*
* @info: The framebuffer to blank.
*
* Framebuffer layer request to change the power state.
*/
static int s3c_fb_blank(int blank_mode, struct fb_info *info)
{
struct s3c_fb_win *win = info->par;
struct s3c_fb *sfb = win->parent;
unsigned int index = win->index;
u32 wincon;
u32 output_on = sfb->output_on;
/* Check the enabled state to see if we need to be running the
* main LCD interface, as if there are no active windows then
* it is highly likely that we also do not need to output
* anything.
*/
s3c_fb_enable(sfb, sfb->enabled ? 1 : 0);
shadow_protect_win(win, 0);
pm_runtime_put_sync(sfb->dev);
return output_on == sfb->output_on;
}
/**
* s3c_fb_pan_display() - Pan the display.
*
* Note that the offsets can be written to the device at any time, as their
* values are latched at each vsync automatically. This also means that only
* the last call to this function will have any effect on next vsync, but
* there is no need to sleep waiting for it to prevent tearing.
*
* @var: The screen information to verify.
* @info: The framebuffer device.
*/
static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct s3c_fb_win *win = info->par;
struct s3c_fb *sfb = win->parent;
void __iomem *buf = sfb->regs + win->index * 8;
unsigned int start_boff, end_boff;
pm_runtime_get_sync(sfb->dev);
/* Offset in bytes to the start of the displayed area */
start_boff = var->yoffset * info->fix.line_length;
/* X offset depends on the current bpp */
if (info->var.bits_per_pixel >= 8) {
start_boff += var->xoffset * (info->var.bits_per_pixel >> 3);
} else {
switch (info->var.bits_per_pixel) {
case 4:
start_boff += var->xoffset >> 1;
break;
case 2:
start_boff += var->xoffset >> 2;
break;
case 1:
start_boff += var->xoffset >> 3;
break;
default:
dev_err(sfb->dev, "invalid bpp\n");
pm_runtime_put_sync(sfb->dev);
return -EINVAL;
}
}
/* Offset in bytes to the end of the displayed area */
end_boff = start_boff + info->var.yres * info->fix.line_length;
/* Temporarily turn off per-vsync update from shadow registers until
* both start and end addresses are updated to prevent corruption */
shadow_protect_win(win, 1);
/* We only support waiting for VSYNC for now, so it's safe
* to always disable irqs here.
*/
s3c_fb_disable_irq(sfb);
spin_unlock(&sfb->slock);
return IRQ_HANDLED;
}
/**
* s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout
* @sfb: main hardware state
* @crtc: head index.
*/
static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc)
{
unsigned long count;
int ret;
/**
* s3c_fb_missing_pixclock() - calculates pixel clock
* @mode: The video mode to change.
*
* Calculate the pixel clock when none has been given through platform data.
*/
static void s3c_fb_missing_pixclock(struct fb_videomode *mode)
{
u64 pixclk = 1000000000000ULL;
u32 div;
div = mode->left_margin + mode->hsync_len + mode->right_margin +
mode->xres;
div *= mode->upper_margin + mode->vsync_len + mode->lower_margin +
mode->yres;
div *= mode->refresh ? : 60;
do_div(pixclk, div);
mode->pixclock = pixclk;
}
/**
* s3c_fb_alloc_memory() - allocate display memory for framebuffer window
* @sfb: The base resources for the hardware.
* @win: The window to initialise memory for.
*
* Allocate memory for the given framebuffer.
*/
static int s3c_fb_alloc_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
{
struct s3c_fb_pd_win *windata = win->windata;
unsigned int real_size, virt_size, size;
struct fb_info *fbi = win->fbinfo;
dma_addr_t map_dma;
dev_dbg(sfb->dev, "allocating memory for display\n");
/**
* s3c_fb_free_memory() - free the display memory for the given window
* @sfb: The base resources for the hardware.
* @win: The window to free the display memory for.
*
* Free the display memory allocated by s3c_fb_alloc_memory().
*/
static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
{
struct fb_info *fbi = win->fbinfo;
if (fbi->screen_buffer)
dma_free_wc(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
fbi->screen_buffer, fbi->fix.smem_start);
}
/**
* s3c_fb_release_win() - release resources for a framebuffer window.
* @sfb: The base resources for the hardware.
* @win: The window to cleanup the resources for.
*
* Release the resources that where claimed for the hardware window,
* such as the framebuffer instance and any memory claimed for it.
*/
static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
{
u32 data;
if (win->fbinfo) {
if (sfb->variant.has_shadowcon) {
data = readl(sfb->regs + SHADOWCON);
data &= ~SHADOWCON_CHx_ENABLE(win->index);
data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index);
writel(data, sfb->regs + SHADOWCON);
}
unregister_framebuffer(win->fbinfo);
if (win->fbinfo->cmap.len)
fb_dealloc_cmap(&win->fbinfo->cmap);
s3c_fb_free_memory(sfb, win);
framebuffer_release(win->fbinfo);
}
}
/**
* s3c_fb_probe_win() - register an hardware window
* @sfb: The base resources for the hardware
* @win_no: The window number
* @variant: The variant information for this window.
* @res: Pointer to where to place the resultant window.
*
* Allocate and do the basic initialisation for one of the hardware's graphics
* windows.
*/
static int s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
struct s3c_fb_win_variant *variant,
struct s3c_fb_win **res)
{
struct fb_videomode initmode;
struct s3c_fb_pd_win *windata;
struct s3c_fb_win *win;
struct fb_info *fbinfo;
int palette_size;
int ret;
/* setup the initial video mode from the window */
initmode.xres = windata->xres;
initmode.yres = windata->yres;
fb_videomode_to_var(&fbinfo->var, &initmode);
/**
* s3c_fb_set_rgb_timing() - set video timing for rgb interface.
* @sfb: The base resources for the hardware.
*
* Set horizontal and vertical lcd rgb interface timing.
*/
static void s3c_fb_set_rgb_timing(struct s3c_fb *sfb)
{
struct fb_videomode *vmode = sfb->pdata->vtiming;
void __iomem *regs = sfb->regs;
int clkdiv;
u32 data;
if (!vmode->pixclock)
s3c_fb_missing_pixclock(vmode);
/**
* s3c_fb_clear_win() - clear hardware window registers.
* @sfb: The base resources for the hardware.
* @win: The window to process.
*
* Reset the specific window registers to a known state.
*/
static void s3c_fb_clear_win(struct s3c_fb *sfb, int win)
{
void __iomem *regs = sfb->regs;
u32 reg;
sfb->bus_clk = devm_clk_get(dev, "lcd");
if (IS_ERR(sfb->bus_clk))
return dev_err_probe(dev, PTR_ERR(sfb->bus_clk),
"failed to get bus clock\n");
clk_prepare_enable(sfb->bus_clk);
if (!sfb->variant.has_clksel) {
sfb->lcd_clk = devm_clk_get(dev, "sclk_fimd");
if (IS_ERR(sfb->lcd_clk)) {
ret = dev_err_probe(dev, PTR_ERR(sfb->lcd_clk),
"failed to get lcd clock\n");
goto err_bus_clk;
}
clk_prepare_enable(sfb->lcd_clk);
}
pm_runtime_enable(sfb->dev);
sfb->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(sfb->regs)) {
ret = PTR_ERR(sfb->regs);
goto err_lcd_clk;
}
sfb->irq_no = platform_get_irq(pdev, 0);
if (sfb->irq_no < 0) {
ret = -ENOENT;
goto err_lcd_clk;
}
ret = devm_request_irq(dev, sfb->irq_no, s3c_fb_irq, 0, "s3c_fb", sfb);
if (ret) {
dev_err(dev, "irq request failed\n");
goto err_lcd_clk;
}
if (!sfb->variant.has_clksel)
clk_disable_unprepare(sfb->lcd_clk);
err_bus_clk:
clk_disable_unprepare(sfb->bus_clk);
return ret;
}
/**
* s3c_fb_remove() - Cleanup on module finalisation
* @pdev: The platform device we are bound to.
*
* Shutdown and then release all the resources that the driver allocated
* on initialisation.
*/
static void s3c_fb_remove(struct platform_device *pdev)
{
struct s3c_fb *sfb = platform_get_drvdata(pdev);
int win;
pm_runtime_get_sync(sfb->dev);
for (win = 0; win < S3C_FB_MAX_WIN; win++)
if (sfb->windows[win])
s3c_fb_release_win(sfb, sfb->windows[win]);
if (!sfb->variant.has_clksel)
clk_disable_unprepare(sfb->lcd_clk);
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.570Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.