static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
			     struct fb_info *info)
{
	struct device *dev = info->device;
	struct atmel_lcdfb_info *sinfo = info->par;
	unsigned long clk_value_khz;

	clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;

	dev_dbg(dev, "%s:\n", __func__);

	if (!(var->pixclock && var->bits_per_pixel)) {
		
		if (!atmel_lcdfb_choose_mode(var, info)) {
			dev_err(dev, "needed value not specified\n");
			return -EINVAL;
		}
	}

	dev_dbg(dev, "  resolution: %ux%u\n", var->xres, var->yres);
	dev_dbg(dev, "  pixclk:     %lu KHz\n", PICOS2KHZ(var->pixclock));
	dev_dbg(dev, "  bpp:        %u\n", var->bits_per_pixel);
	dev_dbg(dev, "  clk:        %lu KHz\n", clk_value_khz);

	if (PICOS2KHZ(var->pixclock) > clk_value_khz) {
		dev_err(dev, "%lu KHz pixel clock is too fast\n", PICOS2KHZ(var->pixclock));
		return -EINVAL;
	}

	
	if (var->xres > var->xres_virtual)
		var->xres_virtual = var->xres;

	if (var->yres > var->yres_virtual)
		var->yres_virtual = var->yres;

	
	var->xres = (var->xres + 3) & ~3UL;
	var->xres_virtual = (var->xres_virtual + 3) & ~3UL;

	var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0;
	var->transp.msb_right = 0;
	var->transp.offset = var->transp.length = 0;
	var->xoffset = var->yoffset = 0;

	if (info->fix.smem_len) {
		unsigned int smem_len = (var->xres_virtual * var->yres_virtual
					 * ((var->bits_per_pixel + 7) / 8));
		if (smem_len > info->fix.smem_len)
			return -EINVAL;
	}

	
	var->vsync_len = min_t(u32, var->vsync_len,
			(ATMEL_LCDC_VPW >> ATMEL_LCDC_VPW_OFFSET) + 1);
	var->upper_margin = min_t(u32, var->upper_margin,
			ATMEL_LCDC_VBP >> ATMEL_LCDC_VBP_OFFSET);
	var->lower_margin = min_t(u32, var->lower_margin,
			ATMEL_LCDC_VFP);
	var->right_margin = min_t(u32, var->right_margin,
			(ATMEL_LCDC_HFP >> ATMEL_LCDC_HFP_OFFSET) + 1);
	var->hsync_len = min_t(u32, var->hsync_len,
			(ATMEL_LCDC_HPW >> ATMEL_LCDC_HPW_OFFSET) + 1);
	var->left_margin = min_t(u32, var->left_margin,
			ATMEL_LCDC_HBP + 1);

	
	var->vsync_len = max_t(u32, var->vsync_len, 1);
	var->right_margin = max_t(u32, var->right_margin, 1);
	var->hsync_len = max_t(u32, var->hsync_len, 1);
	var->left_margin = max_t(u32, var->left_margin, 1);

	switch (var->bits_per_pixel) {
	case 1:
	case 2:
	case 4:
	case 8:
		var->red.offset = var->green.offset = var->blue.offset = 0;
		var->red.length = var->green.length = var->blue.length
			= var->bits_per_pixel;
		break;
	case 16:
		/* Older SOCs use IBGR:555 rather than BGR:565. */
		if (sinfo->have_intensity_bit)
			var->green.length = 5;
		else
			var->green.length = 6;

		if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) {
			/* RGB:5X5 mode */
			var->red.offset = var->green.length + 5;
			var->blue.offset = 0;
		} else {
			/* BGR:5X5 mode */
			var->red.offset = 0;
			var->blue.offset = var->green.length + 5;
		}
		var->green.offset = 5;
		var->red.length = var->blue.length = 5;
		break;
	case 32:
		var->transp.offset = 24;
		var->transp.length = 8;
		
	case 24:
		if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) {
			
			var->red.offset = 16;
			var->blue.offset = 0;
		} else {
			
			var->red.offset = 0;
			var->blue.offset = 16;
		}
		var->green.offset = 8;
		var->red.length = var->green.length = var->blue.length = 8;
		break;
	default:
		dev_err(dev, "color depth %d not supported\n",
					var->bits_per_pixel);
		return -EINVAL;
	}

	return 0;
}
Example #2
0
/**
 *      atmel_lcdfb_check_var - Validates a var passed in.
 *      @var: frame buffer variable screen structure
 *      @info: frame buffer structure that represents a single frame buffer
 *
 *	Checks to see if the hardware supports the state requested by
 *	var passed in. This function does not alter the hardware
 *	state!!!  This means the data stored in struct fb_info and
 *	struct atmel_lcdfb_info do not change. This includes the var
 *	inside of struct fb_info.  Do NOT change these. This function
 *	can be called on its own if we intent to only test a mode and
 *	not actually set it. The stuff in modedb.c is a example of
 *	this. If the var passed in is slightly off by what the
 *	hardware can support then we alter the var PASSED in to what
 *	we can do. If the hardware doesn't support mode change a
 *	-EINVAL will be returned by the upper layers. You don't need
 *	to implement this function then. If you hardware doesn't
 *	support changing the resolution then this function is not
 *	needed. In this case the driver would just provide a var that
 *	represents the static state the screen is in.
 *
 *	Returns negative errno on error, or zero on success.
 */
static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
                                 struct fb_info *info)
{
    struct device *dev = info->device;
    struct atmel_lcdfb_info *sinfo = info->par;
    unsigned long clk_value_khz;

    clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;

    dev_dbg(dev, "%s:\n", __func__);

    if (!(var->pixclock && var->bits_per_pixel)) {
        /* choose a suitable mode if possible */
        if (!atmel_lcdfb_choose_mode(var, info)) {
            dev_err(dev, "needed value not specified\n");
            return -EINVAL;
        }
    }

    dev_dbg(dev, "  resolution: %ux%u\n", var->xres, var->yres);
    dev_dbg(dev, "  pixclk:     %lu KHz\n", PICOS2KHZ(var->pixclock));
    dev_dbg(dev, "  bpp:        %u\n", var->bits_per_pixel);
    dev_dbg(dev, "  clk:        %lu KHz\n", clk_value_khz);

    if ((PICOS2KHZ(var->pixclock) * var->bits_per_pixel / 8) > clk_value_khz) {
        dev_err(dev, "%lu KHz pixel clock is too fast\n", PICOS2KHZ(var->pixclock));
        return -EINVAL;
    }

    /* Do not allow to have real resoulution larger than virtual */
    if (var->xres > var->xres_virtual)
        var->xres_virtual = var->xres;

    if (var->yres > var->yres_virtual)
        var->yres_virtual = var->yres;

    /* Force same alignment for each line */
    var->xres = (var->xres + 3) & ~3UL;
    var->xres_virtual = (var->xres_virtual + 3) & ~3UL;

    var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0;
    var->transp.msb_right = 0;
    var->transp.offset = var->transp.length = 0;
    var->xoffset = var->yoffset = 0;

    /* Saturate vertical and horizontal timings at maximum values */
    var->vsync_len = min_t(u32, var->vsync_len,
                           (ATMEL_LCDC_VPW >> ATMEL_LCDC_VPW_OFFSET) + 1);
    var->upper_margin = min_t(u32, var->upper_margin,
                              ATMEL_LCDC_VBP >> ATMEL_LCDC_VBP_OFFSET);
    var->lower_margin = min_t(u32, var->lower_margin,
                              ATMEL_LCDC_VFP);
    var->right_margin = min_t(u32, var->right_margin,
                              (ATMEL_LCDC_HFP >> ATMEL_LCDC_HFP_OFFSET) + 1);
    var->hsync_len = min_t(u32, var->hsync_len,
                           (ATMEL_LCDC_HPW >> ATMEL_LCDC_HPW_OFFSET) + 1);
    var->left_margin = min_t(u32, var->left_margin,
                             ATMEL_LCDC_HBP + 1);

    /* Some parameters can't be zero */
    var->vsync_len = max_t(u32, var->vsync_len, 1);
    var->right_margin = max_t(u32, var->right_margin, 1);
    var->hsync_len = max_t(u32, var->hsync_len, 1);
    var->left_margin = max_t(u32, var->left_margin, 1);

    switch (var->bits_per_pixel) {
    case 1:
    case 2:
    case 4:
    case 8:
        var->red.offset = var->green.offset = var->blue.offset = 0;
        var->red.length = var->green.length = var->blue.length
                                              = var->bits_per_pixel;
        break;
    case 15:
    case 16:
        if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) {
            /* RGB:565 mode */
            var->red.offset = 11;
            var->blue.offset = 0;
            var->green.length = 6;
        } else {
            /* BGR:555 mode */
            var->red.offset = 0;
            var->blue.offset = 10;
            var->green.length = 5;
        }
        var->green.offset = 5;
        var->red.length = var->blue.length = 5;
        break;
    case 32:
        var->transp.offset = 24;
        var->transp.length = 8;
    /* fall through */
    case 24:
        if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) {
            /* RGB:888 mode */
            var->red.offset = 16;
            var->blue.offset = 0;
        } else {
            /* BGR:888 mode */
            var->red.offset = 0;
            var->blue.offset = 16;
        }
        var->green.offset = 8;
        var->red.length = var->green.length = var->blue.length = 8;
        break;
    default:
        dev_err(dev, "color depth %d not supported\n",
                var->bits_per_pixel);
        return -EINVAL;
    }

    return 0;
}