// SPDX-License-Identifier: GPL-2.0-only /* * Freescale i.MX Frame Buffer device driver * * Copyright (C) 2004 Sascha Hauer, Pengutronix * Based on acornfb.c Copyright (C) Russell King. * * Please direct your questions and comments on this driver to the following * email address: * * linux-arm-kernel@lists.arm.linux.org.uk
*/
/* Used fb-mode. Can be set on kernel command line, therefore file-static. */ staticconstchar *fb_mode;
/* * These are the bitfields for each * display depth that we support.
*/ struct imxfb_rgb { struct fb_bitfield red; struct fb_bitfield green; struct fb_bitfield blue; struct fb_bitfield transp;
};
/* * Minimum X and Y resolutions
*/ #define MIN_XRES 64 #define MIN_YRES 64
/* Actually this really is 18bit support, the lowest 2 bits of each colour * are unused in hardware. We claim to have 24bit support to make software * like X work, which does not support 18bit.
*/ staticstruct imxfb_rgb def_rgb_18 = {
.red = {.offset = 16, .length = 8,},
.green = {.offset = 8, .length = 8,},
.blue = {.offset = 0, .length = 8,},
.transp = {.offset = 0, .length = 0,},
};
/* * If inverse mode was selected, invert all the colours * rather than the register number. The register number * is what you poke into the framebuffer to produce the * colour you requested.
*/ if (fbi->cmap_inverse) {
red = 0xffff - red;
green = 0xffff - green;
blue = 0xffff - blue;
}
/* * If greyscale is true, then we convert the RGB value * to greyscale no mater what visual we are using.
*/ if (info->var.grayscale)
red = green = blue = (19595 * red + 38470 * green +
7471 * blue) >> 16;
switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: /* * 12 or 16-bit True Colour. We encode the RGB value * according to the RGB bitfield information.
*/ 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;
ret = 0;
} break;
case FB_VISUAL_STATIC_PSEUDOCOLOR: case FB_VISUAL_PSEUDOCOLOR:
ret = imxfb_setpalettereg(regno, red, green, blue, trans, info); break;
}
fbi->pcr = pcr; /* * The LCDC AUS Mode Control Register does not exist on imx1.
*/ if (!is_imx1_fb(fbi) && imxfb_mode->aus_mode)
fbi->lauscr = LAUSCR_AUS_MODE;
/* * Copy the RGB parameters for this display * from the machine specific parameters.
*/
var->red = rgb->red;
var->green = rgb->green;
var->blue = rgb->blue;
var->transp = rgb->transp;
/* * imxfb_set_par(): * Set the user defined part of the display for the specified console
*/ staticint imxfb_set_par(struct fb_info *info)
{ struct imxfb_info *fbi = info->par; struct fb_var_screeninfo *var = &info->var;
if (var->bits_per_pixel == 16 || var->bits_per_pixel == 32)
info->fix.visual = FB_VISUAL_TRUECOLOR; elseif (!fbi->cmap_static)
info->fix.visual = FB_VISUAL_PSEUDOCOLOR; else { /* * Some people have weird ideas about wanting static * pseudocolor maps. I suspect their user space * applications are broken.
*/
info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
}
switch (blank) { case FB_BLANK_POWERDOWN: case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_NORMAL:
imxfb_disable_controller(fbi); break;
case FB_BLANK_UNBLANK: return imxfb_enable_controller(fbi);
} return 0;
}
/* * imxfb_activate_var(): * Configures LCD Controller based on entries in var parameter. Settings are * only written to the controller if changes were made.
*/ staticint imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info)
{ struct imxfb_info *fbi = info->par;
u32 ymax_mask = is_imx1_fb(fbi) ? YMAX_MASK_IMX1 : YMAX_MASK_IMX21;
u8 left_margin_low;
of_id = of_match_device(imxfb_of_dev_id, &pdev->dev); if (of_id)
pdev->id_entry = of_id->data;
info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev); if (!info) return -ENOMEM;
fbi = info->par;
platform_set_drvdata(pdev, info);
ret = imxfb_init_fbinfo(pdev); if (ret < 0) goto failed_init;
fb_mode = NULL;
display_np = of_parse_phandle(pdev->dev.of_node, "display", 0); if (!display_np) {
dev_err(&pdev->dev, "No display defined in devicetree\n");
ret = -EINVAL; goto failed_init;
}
/* * imxfb does not support more modes, we choose only the native * mode.
*/
fbi->num_modes = 1;
fbi->mode = devm_kzalloc(&pdev->dev, sizeof(struct imx_fb_videomode), GFP_KERNEL); if (!fbi->mode) {
ret = -ENOMEM;
of_node_put(display_np); goto failed_init;
}
ret = imxfb_of_read_mode(&pdev->dev, display_np, fbi->mode);
of_node_put(display_np); if (ret) goto failed_init;
/* * Calculate maximum bytes used per pixel. In most cases this should * be the same as m->bpp/8
*/
m = &fbi->mode[0];
bytes_per_pixel = (m->bpp + 7) / 8; for (i = 0; i < fbi->num_modes; i++, m++)
info->fix.smem_len = max_t(size_t, info->fix.smem_len,
m->mode.xres * m->mode.yres * bytes_per_pixel);
fbi->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(fbi->clk_ipg)) {
ret = PTR_ERR(fbi->clk_ipg); goto failed_init;
}
/* * The LCDC controller does not have an enable bit. The * controller starts directly when the clocks are enabled. * If the clocks are enabled when the controller is not yet * programmed with proper register values (enabled at the * bootloader, for example) then it just goes into some undefined * state. * To avoid this issue, let's enable and disable LCDC IPG clock * so that we force some kind of 'reset' to the LCDC block.
*/
ret = clk_prepare_enable(fbi->clk_ipg); if (ret) goto failed_init;
clk_disable_unprepare(fbi->clk_ipg);
fbi->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(fbi->clk_ahb)) {
ret = PTR_ERR(fbi->clk_ahb); goto failed_init;
}
fbi->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(fbi->clk_per)) {
ret = PTR_ERR(fbi->clk_per); goto failed_init;
}
fbi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(fbi->regs)) {
ret = PTR_ERR(fbi->regs); goto failed_init;
}
fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
info->screen_buffer = dma_alloc_wc(&pdev->dev, fbi->map_size,
&fbi->map_dma, GFP_KERNEL); if (!info->screen_buffer) {
dev_err(&pdev->dev, "Failed to allocate video RAM\n");
ret = -ENOMEM; goto failed_init;
}
info->fix.smem_start = fbi->map_dma;
INIT_LIST_HEAD(&info->modelist); for (i = 0; i < fbi->num_modes; i++) {
ret = fb_add_videomode(&fbi->mode[i].mode, &info->modelist); if (ret) {
dev_err(&pdev->dev, "Failed to add videomode\n"); goto failed_cmap;
}
}
/* * This makes sure that our colour bitfield * descriptors are correctly initialised.
*/
imxfb_check_var(&info->var, info);
/* * For modes > 8bpp, the color map is bypassed. * Therefore, 256 entries are enough.
*/
ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret < 0) goto failed_cmap;
imxfb_set_par(info);
fbi->lcd_pwr = devm_regulator_get(&pdev->dev, "lcd"); if (PTR_ERR(fbi->lcd_pwr) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER; goto failed_lcd;
}
lcd = devm_lcd_device_register(&pdev->dev, "imxfb-lcd", &pdev->dev, fbi,
&imxfb_lcd_ops); if (IS_ERR(lcd)) {
ret = PTR_ERR(lcd); goto failed_lcd;
}
lcd->props.max_contrast = 0xff;
info->lcd_dev = lcd;
ret = register_framebuffer(info); if (ret < 0) {
dev_err(&pdev->dev, "failed to register framebuffer\n"); goto failed_lcd;
}
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.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.