Project import generated by Copybara. GitOrigin-RevId: 0acb173e07e155358594d3ce7b5f19e91bb9fec3
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 813a264..cac3e35 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c
@@ -630,6 +630,12 @@ struct backlight_device *of_find_backlight(struct device *dev) of_node_put(np); if (!bd) return ERR_PTR(-EPROBE_DEFER); + /* + * Note: gpio_backlight uses brightness as + * power state during probe + */ + if (!bd->props.brightness) + bd->props.brightness = bd->props.max_brightness; } }
diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c index 8096202..2d8e819 100644 --- a/drivers/video/backlight/lm3630a_bl.c +++ b/drivers/video/backlight/lm3630a_bl.c
@@ -188,7 +188,7 @@ static int lm3630a_bank_a_update_status(struct backlight_device *bl) if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) { lm3630a_pwm_ctrl(pchip, bl->props.brightness, bl->props.max_brightness); - return 0; + return bl->props.brightness; } /* disable sleep */ @@ -208,8 +208,8 @@ static int lm3630a_bank_a_update_status(struct backlight_device *bl) return 0; out_i2c_err: - dev_err(pchip->dev, "i2c failed to access (%pe)\n", ERR_PTR(ret)); - return ret; + dev_err(pchip->dev, "i2c failed to access\n"); + return bl->props.brightness; } static int lm3630a_bank_a_get_brightness(struct backlight_device *bl) @@ -265,7 +265,7 @@ static int lm3630a_bank_b_update_status(struct backlight_device *bl) if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) { lm3630a_pwm_ctrl(pchip, bl->props.brightness, bl->props.max_brightness); - return 0; + return bl->props.brightness; } /* disable sleep */ @@ -285,8 +285,8 @@ static int lm3630a_bank_b_update_status(struct backlight_device *bl) return 0; out_i2c_err: - dev_err(pchip->dev, "i2c failed to access (%pe)\n", ERR_PTR(ret)); - return ret; + dev_err(pchip->dev, "i2c failed to access REG_CTRL\n"); + return bl->props.brightness; } static int lm3630a_bank_b_get_brightness(struct backlight_device *bl) @@ -480,10 +480,8 @@ static int lm3630a_parse_node(struct lm3630a_chip *pchip, device_for_each_child_node(pchip->dev, node) { ret = lm3630a_parse_bank(pdata, node, &seen_led_sources); - if (ret) { - fwnode_handle_put(node); + if (ret) return ret; - } } return ret;
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 118492b..746eebc 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c
@@ -415,33 +415,6 @@ static bool pwm_backlight_is_linear(struct platform_pwm_backlight_data *data) static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb) { struct device_node *node = pb->dev->of_node; - bool active = true; - - /* - * If the enable GPIO is present, observable (either as input - * or output) and off then the backlight is not currently active. - * */ - if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0) - active = false; - - if (!regulator_is_enabled(pb->power_supply)) - active = false; - - if (!pwm_is_enabled(pb->pwm)) - active = false; - - /* - * Synchronize the enable_gpio with the observed state of the - * hardware. - */ - if (pb->enable_gpio) - gpiod_direction_output(pb->enable_gpio, active); - - /* - * Do not change pb->enabled here! pb->enabled essentially - * tells us if we own one of the regulator's use counts and - * right now we do not. - */ /* Not booted with device tree or no phandle link to the node */ if (!node || !node->phandle) @@ -453,7 +426,20 @@ static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb) * assume that another driver will enable the backlight at the * appropriate time. Therefore, if it is disabled, keep it so. */ - return active ? FB_BLANK_UNBLANK: FB_BLANK_POWERDOWN; + + /* if the enable GPIO is disabled, do not enable the backlight */ + if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0) + return FB_BLANK_POWERDOWN; + + /* The regulator is disabled, do not enable the backlight */ + if (!regulator_is_enabled(pb->power_supply)) + return FB_BLANK_POWERDOWN; + + /* The PWM is disabled, keep it like this */ + if (!pwm_is_enabled(pb->pwm)) + return FB_BLANK_POWERDOWN; + + return FB_BLANK_UNBLANK; } static int pwm_backlight_probe(struct platform_device *pdev) @@ -522,6 +508,18 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->enable_gpio = gpio_to_desc(data->enable_gpio); } + /* + * If the GPIO is not known to be already configured as output, that + * is, if gpiod_get_direction returns either 1 or -EINVAL, change the + * direction to output and set the GPIO as active. + * Do not force the GPIO to active when it was already output as it + * could cause backlight flickering or we would enable the backlight too + * early. Leave the decision of the initial backlight state for later. + */ + if (pb->enable_gpio && + gpiod_get_direction(pb->enable_gpio) != 0) + gpiod_direction_output(pb->enable_gpio, 1); + pb->power_supply = devm_regulator_get(&pdev->dev, "power"); if (IS_ERR(pb->power_supply)) { ret = PTR_ERR(pb->power_supply); @@ -618,8 +616,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->scale = data->max_brightness; } - pb->lth_brightness = data->lth_brightness * (div_u64(state.period, - pb->scale)); + pb->lth_brightness = data->lth_brightness * (state.period / pb->scale); props.type = BACKLIGHT_RAW; props.max_brightness = data->max_brightness;
diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 559a730..79c9bd8 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c
@@ -291,13 +291,13 @@ static unsigned long sticon_getxy(struct vc_data *conp, unsigned long pos, static u8 sticon_build_attr(struct vc_data *conp, u8 color, u8 intens, u8 blink, u8 underline, u8 reverse, u8 italic) { - u8 fg = color & 7; - u8 bg = (color & 0x70) >> 4; + u8 attr = ((color & 0x70) >> 1) | ((color & 7)); - if (reverse) - return (fg << 3) | bg; - else - return (bg << 3) | fg; + if (reverse) { + color = ((color >> 3) & 0x7) | ((color & 0x7) << 3); + } + + return attr; } static void sticon_invert_region(struct vc_data *conp, u16 *p, int count)
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 23f15f4..55507df 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c
@@ -370,17 +370,11 @@ static void vgacon_init(struct vc_data *c, int init) struct uni_pagedir *p; /* - * We cannot be loaded as a module, therefore init will be 1 - * if we are the default console, however if we are a fallback - * console, for example if fbcon has failed registration, then - * init will be 0, so we need to make sure our boot parameters - * have been copied to the console structure for vgacon_resize - * ultimately called by vc_resize. Any subsequent calls to - * vgacon_init init will have init set to 0 too. + * We cannot be loaded as a module, therefore init is always 1, + * but vgacon_init can be called more than once, and init will + * not be 1. */ c->vc_can_do_color = vga_can_do_color; - c->vc_scan_lines = vga_scan_lines; - c->vc_font.height = c->vc_cell_height = vga_video_font_height; /* set dimensions manually if init != 0 since vc_resize() will fail */ if (init) { @@ -389,6 +383,8 @@ static void vgacon_init(struct vc_data *c, int init) } else vc_resize(c, vga_video_num_columns, vga_video_num_lines); + c->vc_scan_lines = vga_scan_lines; + c->vc_font.height = vga_video_font_height; c->vc_complement_mask = 0x7700; if (vga_512_chars) c->vc_hi_font_mask = 0x0800; @@ -521,32 +517,32 @@ static void vgacon_cursor(struct vc_data *c, int mode) switch (c->vc_cursor_type & 0x0f) { case CUR_UNDERLINE: vgacon_set_cursor_size(c->vc_x, - c->vc_cell_height - - (c->vc_cell_height < + c->vc_font.height - + (c->vc_font.height < 10 ? 2 : 3), - c->vc_cell_height - - (c->vc_cell_height < + c->vc_font.height - + (c->vc_font.height < 10 ? 1 : 2)); break; case CUR_TWO_THIRDS: vgacon_set_cursor_size(c->vc_x, - c->vc_cell_height / 3, - c->vc_cell_height - - (c->vc_cell_height < + c->vc_font.height / 3, + c->vc_font.height - + (c->vc_font.height < 10 ? 1 : 2)); break; case CUR_LOWER_THIRD: vgacon_set_cursor_size(c->vc_x, - (c->vc_cell_height * 2) / 3, - c->vc_cell_height - - (c->vc_cell_height < + (c->vc_font.height * 2) / 3, + c->vc_font.height - + (c->vc_font.height < 10 ? 1 : 2)); break; case CUR_LOWER_HALF: vgacon_set_cursor_size(c->vc_x, - c->vc_cell_height / 2, - c->vc_cell_height - - (c->vc_cell_height < + c->vc_font.height / 2, + c->vc_font.height - + (c->vc_font.height < 10 ? 1 : 2)); break; case CUR_NONE: @@ -557,7 +553,7 @@ static void vgacon_cursor(struct vc_data *c, int mode) break; default: vgacon_set_cursor_size(c->vc_x, 1, - c->vc_cell_height); + c->vc_font.height); break; } break; @@ -568,13 +564,13 @@ static int vgacon_doresize(struct vc_data *c, unsigned int width, unsigned int height) { unsigned long flags; - unsigned int scanlines = height * c->vc_cell_height; + unsigned int scanlines = height * c->vc_font.height; u8 scanlines_lo = 0, r7 = 0, vsync_end = 0, mode, max_scan; raw_spin_lock_irqsave(&vga_lock, flags); vgacon_xres = width * VGA_FONTWIDTH; - vgacon_yres = height * c->vc_cell_height; + vgacon_yres = height * c->vc_font.height; if (vga_video_type >= VIDEO_TYPE_VGAC) { outb_p(VGA_CRTC_MAX_SCAN, vga_video_port_reg); max_scan = inb_p(vga_video_port_val); @@ -629,9 +625,9 @@ static int vgacon_doresize(struct vc_data *c, static int vgacon_switch(struct vc_data *c) { int x = c->vc_cols * VGA_FONTWIDTH; - int y = c->vc_rows * c->vc_cell_height; + int y = c->vc_rows * c->vc_font.height; int rows = screen_info.orig_video_lines * vga_default_font_height/ - c->vc_cell_height; + c->vc_font.height; /* * We need to save screen size here as it's the only way * we can spot the screen has been resized and we need to @@ -1062,7 +1058,7 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight) cursor_size_lastto = 0; c->vc_sw->con_cursor(c, CM_DRAW); } - c->vc_font.height = c->vc_cell_height = fontheight; + c->vc_font.height = fontheight; vc_resize(c, 0, rows); /* Adjust console size */ } } @@ -1110,20 +1106,12 @@ static int vgacon_resize(struct vc_data *c, unsigned int width, if ((width << 1) * height > vga_vram_size) return -EINVAL; - if (user) { - /* - * Ho ho! Someone (svgatextmode, eh?) may have reprogrammed - * the video mode! Set the new defaults then and go away. - */ - screen_info.orig_video_cols = width; - screen_info.orig_video_lines = height; - vga_default_font_height = c->vc_cell_height; - return 0; - } if (width % 2 || width > screen_info.orig_video_cols || height > (screen_info.orig_video_lines * vga_default_font_height)/ - c->vc_cell_height) - return -EINVAL; + c->vc_font.height) + /* let svgatextmode tinker with video timings and + return success */ + return (user) ? 0 : -EINVAL; if (con_is_visible(c) && !vga_is_gfx) /* who knows */ vgacon_doresize(c, width, height);
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index a7e5f12..f0f1702 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig
@@ -1269,7 +1269,6 @@ select FB_CFB_IMAGEBLIT select FB_BACKLIGHT if FB_ATY_BACKLIGHT select FB_MACMODES if PPC - select FB_ATY_CT if SPARC64 && PCI help This driver supports graphics boards with the ATI Mach64 chips. Say Y if you have such a graphics board. @@ -1280,6 +1279,7 @@ config FB_ATY_CT bool "Mach64 CT/VT/GT/LT (incl. 3D RAGE) support" depends on PCI && FB_ATY + default y if SPARC64 && PCI help Say Y here to support use of ATI's 64-bit Rage boards (or other boards based on the Mach64 CT, VT, GT, and LT chipsets) as a @@ -2266,3 +2266,4 @@ source "drivers/video/fbdev/omap/Kconfig" source "drivers/video/fbdev/omap2/Kconfig" source "drivers/video/fbdev/mmp/Kconfig" +source "drivers/video/fbdev/qti/Kconfig"
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index aa63527..e338e16 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_FB_HYPERV) += hyperv_fb.o obj-$(CONFIG_FB_OPENCORES) += ocfb.o obj-$(CONFIG_FB_SM712) += sm712fb.o +obj-y += qti/ # Platform or fallback drivers go here obj-$(CONFIG_FB_UVESA) += uvesafb.o
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c index 3b7a7c7..7de43be 100644 --- a/drivers/video/fbdev/amba-clcd.c +++ b/drivers/video/fbdev/amba-clcd.c
@@ -774,15 +774,12 @@ static int clcdfb_of_vram_setup(struct clcd_fb *fb) return -ENODEV; fb->fb.screen_base = of_iomap(memory, 0); - if (!fb->fb.screen_base) { - of_node_put(memory); + if (!fb->fb.screen_base) return -ENOMEM; - } fb->fb.fix.smem_start = of_translate_address(memory, of_get_address(memory, 0, &size, NULL)); fb->fb.fix.smem_len = size; - of_node_put(memory); return 0; }
diff --git a/drivers/video/fbdev/asiliantfb.c b/drivers/video/fbdev/asiliantfb.c index c1d6e63..ea31054 100644 --- a/drivers/video/fbdev/asiliantfb.c +++ b/drivers/video/fbdev/asiliantfb.c
@@ -227,9 +227,6 @@ static int asiliantfb_check_var(struct fb_var_screeninfo *var, { unsigned long Ftarget, ratio, remainder; - if (!var->pixclock) - return -EINVAL; - ratio = 1000000 / var->pixclock; remainder = 1000000 % var->pixclock; Ftarget = 1000000 * ratio + (1000000 * remainder) / var->pixclock;
diff --git a/drivers/video/fbdev/atafb.c b/drivers/video/fbdev/atafb.c index c1ee817..51f5d1c 100644 --- a/drivers/video/fbdev/atafb.c +++ b/drivers/video/fbdev/atafb.c
@@ -1692,9 +1692,9 @@ static int falcon_setcolreg(unsigned int regno, unsigned int red, ((blue & 0xfc00) >> 8)); if (regno < 16) { shifter_tt.color_reg[regno] = - ((((red & 0xe000) >> 13) | ((red & 0x1000) >> 12)) << 8) | - ((((green & 0xe000) >> 13) | ((green & 0x1000) >> 12)) << 4) | - ((blue & 0xe000) >> 13) | ((blue & 0x1000) >> 12); + (((red & 0xe000) >> 13) | ((red & 0x1000) >> 12) << 8) | + (((green & 0xe000) >> 13) | ((green & 0x1000) >> 12) << 4) | + ((blue & 0xe000) >> 13) | ((blue & 0x1000) >> 12); ((u32 *)info->pseudo_palette)[regno] = ((red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11)); @@ -1980,9 +1980,9 @@ static int stste_setcolreg(unsigned int regno, unsigned int red, green >>= 12; if (ATARIHW_PRESENT(EXTD_SHIFTER)) shifter_tt.color_reg[regno] = - ((((red & 0xe) >> 1) | ((red & 1) << 3)) << 8) | - ((((green & 0xe) >> 1) | ((green & 1) << 3)) << 4) | - ((blue & 0xe) >> 1) | ((blue & 1) << 3); + (((red & 0xe) >> 1) | ((red & 1) << 3) << 8) | + (((green & 0xe) >> 1) | ((green & 1) << 3) << 4) | + ((blue & 0xe) >> 1) | ((blue & 1) << 3); else shifter_tt.color_reg[regno] = ((red & 0xe) << 7) |
diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c index b919198..cf2bfff 100644 --- a/drivers/video/fbdev/atmel_lcdfb.c +++ b/drivers/video/fbdev/atmel_lcdfb.c
@@ -1062,16 +1062,15 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) INIT_LIST_HEAD(&info->modelist); - if (!pdev->dev.of_node) { + if (pdev->dev.of_node) { + ret = atmel_lcdfb_of_init(sinfo); + if (ret) + goto free_info; + } else { dev_err(dev, "cannot get default configuration\n"); goto free_info; } - ret = atmel_lcdfb_of_init(sinfo); - if (ret) - goto free_info; - - ret = -ENODEV; if (!sinfo->config) goto free_info;
diff --git a/drivers/video/fbdev/chipsfb.c b/drivers/video/fbdev/chipsfb.c index 80fdd3e..f4dc320 100644 --- a/drivers/video/fbdev/chipsfb.c +++ b/drivers/video/fbdev/chipsfb.c
@@ -331,7 +331,7 @@ static const struct fb_var_screeninfo chipsfb_var = { static void init_chips(struct fb_info *p, unsigned long addr) { - fb_memset(p->screen_base, 0, 0x100000); + memset(p->screen_base, 0, 0x100000); p->fix = chipsfb_fix; p->fix.smem_start = addr;
diff --git a/drivers/video/fbdev/cirrusfb.c b/drivers/video/fbdev/cirrusfb.c index 1b0a58f..e4ce566 100644 --- a/drivers/video/fbdev/cirrusfb.c +++ b/drivers/video/fbdev/cirrusfb.c
@@ -470,7 +470,7 @@ static int cirrusfb_check_mclk(struct fb_info *info, long freq) return 0; } -static int cirrusfb_check_pixclock(struct fb_var_screeninfo *var, +static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var, struct fb_info *info) { long freq; @@ -479,7 +479,9 @@ static int cirrusfb_check_pixclock(struct fb_var_screeninfo *var, unsigned maxclockidx = var->bits_per_pixel >> 3; /* convert from ps to kHz */ - freq = PICOS2KHZ(var->pixclock ? : 1); + freq = PICOS2KHZ(var->pixclock); + + dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; cinfo->multiplexing = 0; @@ -487,13 +489,11 @@ static int cirrusfb_check_pixclock(struct fb_var_screeninfo *var, /* If the frequency is greater than we can support, we might be able * to use multiplexing for the video mode */ if (freq > maxclock) { - var->pixclock = KHZ2PICOS(maxclock); - - while ((freq = PICOS2KHZ(var->pixclock)) > maxclock) - var->pixclock++; + dev_err(info->device, + "Frequency greater than maxclock (%ld kHz)\n", + maxclock); + return -EINVAL; } - dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); - /* * Additional constraint: 8bpp uses DAC clock doubling to allow maximum * pixel clock
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index 82c20c6..4766258 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c
@@ -168,6 +168,10 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma) vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; if (!(info->flags & FBINFO_VIRTFB)) vma->vm_flags |= VM_IO; + + if (info->flags & FBINFO_DMAFB) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_private_data = info; return 0; }
diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c index e8a17fb..e5ae33c 100644 --- a/drivers/video/fbdev/core/fbcmap.c +++ b/drivers/video/fbdev/core/fbcmap.c
@@ -101,17 +101,17 @@ int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags) if (!len) return 0; - cmap->red = kzalloc(size, flags); + cmap->red = kmalloc(size, flags); if (!cmap->red) goto fail; - cmap->green = kzalloc(size, flags); + cmap->green = kmalloc(size, flags); if (!cmap->green) goto fail; - cmap->blue = kzalloc(size, flags); + cmap->blue = kmalloc(size, flags); if (!cmap->blue) goto fail; if (transp) { - cmap->transp = kzalloc(size, flags); + cmap->transp = kmalloc(size, flags); if (!cmap->transp) goto fail; } else {
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 8721b75..4cf71ee 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c
@@ -1339,9 +1339,6 @@ static void fbcon_cursor(struct vc_data *vc, int mode) ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; - if (!ops->cursor) - return; - ops->cursor(vc, info, mode, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); } @@ -2058,7 +2055,7 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, return -EINVAL; DPRINTK("resize now %ix%i\n", var.xres, var.yres); - if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) { + if (con_is_visible(vc)) { var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; fb_set_var(info, &var); @@ -2490,11 +2487,6 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, if (charcount != 256 && charcount != 512) return -EINVAL; - /* font bigger than screen resolution ? */ - if (w > FBCON_SWAP(info->var.rotate, info->var.xres, info->var.yres) || - h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres)) - return -EINVAL; - /* Make sure drawing engine can handle the font */ if (!(info->pixmap.blit_x & (1 << (font->width - 1))) || !(info->pixmap.blit_y & (1 << (font->height - 1)))) @@ -2761,34 +2753,6 @@ void fbcon_update_vcs(struct fb_info *info, bool all) } EXPORT_SYMBOL(fbcon_update_vcs); -/* let fbcon check if it supports a new screen resolution */ -int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var) -{ - struct fbcon_ops *ops = info->fbcon_par; - struct vc_data *vc; - unsigned int i; - - WARN_CONSOLE_UNLOCKED(); - - if (!ops) - return 0; - - /* prevent setting a screen size which is smaller than font size */ - for (i = first_fb_vc; i <= last_fb_vc; i++) { - vc = vc_cons[i].d; - if (!vc || vc->vc_mode != KD_TEXT || - registered_fb[con2fb_map[i]] != info) - continue; - - if (vc->vc_font.width > FBCON_SWAP(var->rotate, var->xres, var->yres) || - vc->vc_font.height > FBCON_SWAP(var->rotate, var->yres, var->xres)) - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL_GPL(fbcon_modechange_possible); - int fbcon_mode_deleted(struct fb_info *info, struct fb_videomode *mode) { @@ -3319,9 +3283,6 @@ static void fbcon_register_existing_fbs(struct work_struct *work) console_lock(); - deferred_takeover = false; - logo_shown = FBCON_LOGO_DONTSHOW; - for_each_registered_fb(i) fbcon_fb_registered(registered_fb[i]); @@ -3339,6 +3300,8 @@ static int fbcon_output_notifier(struct notifier_block *nb, pr_info("fbcon: Taking over console\n"); dummycon_unregister_output_notifier(&fbcon_output_nb); + deferred_takeover = false; + logo_shown = FBCON_LOGO_DONTSHOW; /* We may get called in atomic context */ schedule_work(&fbcon_deferred_takeover_work);
diff --git a/drivers/video/fbdev/core/fbcvt.c b/drivers/video/fbdev/core/fbcvt.c index 6484346..55d2bd0 100644 --- a/drivers/video/fbdev/core/fbcvt.c +++ b/drivers/video/fbdev/core/fbcvt.c
@@ -214,11 +214,9 @@ static u32 fb_cvt_aspect_ratio(struct fb_cvt_data *cvt) static void fb_cvt_print_name(struct fb_cvt_data *cvt) { u32 pixcount, pixcount_mod; - int size = 256; - int off = 0; - u8 *buf; + int cnt = 255, offset = 0, read = 0; + u8 *buf = kzalloc(256, GFP_KERNEL); - buf = kzalloc(size, GFP_KERNEL); if (!buf) return; @@ -226,30 +224,43 @@ static void fb_cvt_print_name(struct fb_cvt_data *cvt) pixcount_mod = (cvt->xres * (cvt->yres/cvt->interlace)) % 1000000; pixcount_mod /= 1000; - off += scnprintf(buf + off, size - off, "fbcvt: %dx%d@%d: CVT Name - ", - cvt->xres, cvt->yres, cvt->refresh); + read = snprintf(buf+offset, cnt, "fbcvt: %dx%d@%d: CVT Name - ", + cvt->xres, cvt->yres, cvt->refresh); + offset += read; + cnt -= read; - if (cvt->status) { - off += scnprintf(buf + off, size - off, - "Not a CVT standard - %d.%03d Mega Pixel Image\n", - pixcount, pixcount_mod); - } else { - if (pixcount) - off += scnprintf(buf + off, size - off, "%d", pixcount); + if (cvt->status) + snprintf(buf+offset, cnt, "Not a CVT standard - %d.%03d Mega " + "Pixel Image\n", pixcount, pixcount_mod); + else { + if (pixcount) { + read = snprintf(buf+offset, cnt, "%d", pixcount); + cnt -= read; + offset += read; + } - off += scnprintf(buf + off, size - off, ".%03dM", pixcount_mod); + read = snprintf(buf+offset, cnt, ".%03dM", pixcount_mod); + cnt -= read; + offset += read; if (cvt->aspect_ratio == 0) - off += scnprintf(buf + off, size - off, "3"); + read = snprintf(buf+offset, cnt, "3"); else if (cvt->aspect_ratio == 3) - off += scnprintf(buf + off, size - off, "4"); + read = snprintf(buf+offset, cnt, "4"); else if (cvt->aspect_ratio == 1 || cvt->aspect_ratio == 4) - off += scnprintf(buf + off, size - off, "9"); + read = snprintf(buf+offset, cnt, "9"); else if (cvt->aspect_ratio == 2) - off += scnprintf(buf + off, size - off, "A"); + read = snprintf(buf+offset, cnt, "A"); + else + read = 0; + cnt -= read; + offset += read; - if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) - off += scnprintf(buf + off, size - off, "-R"); + if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) { + read = snprintf(buf+offset, cnt, "-R"); + cnt -= read; + offset += read; + } } printk(KERN_INFO "%s\n", buf);
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 06150a8..bf76dad 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c
@@ -512,7 +512,7 @@ static int fb_show_logo_line(struct fb_info *info, int rotate, while (n && (n * (logo->width + 8) - 8 > xres)) --n; - image.dx = (xres - (n * (logo->width + 8) - 8)) / 2; + image.dx = (xres - n * (logo->width + 8) - 8) / 2; image.dy = y ?: (yres - logo->height) / 2; } else { image.dx = 0; @@ -957,7 +957,6 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) struct fb_var_screeninfo old_var; struct fb_videomode mode; struct fb_event event; - u32 unused; if (var->activate & FB_ACTIVATE_INV_MODE) { struct fb_videomode mode1, mode2; @@ -966,11 +965,13 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) fb_var_to_videomode(&mode2, &info->var); /* make sure we don't delete the videomode of current var */ ret = fb_mode_is_equal(&mode1, &mode2); - if (!ret) { - ret = fbcon_mode_deleted(info, &mode1); - if (!ret) - fb_delete_videomode(&mode1, &info->modelist); - } + + if (!ret) + fbcon_mode_deleted(info, &mode1); + + if (!ret) + fb_delete_videomode(&mode1, &info->modelist); + return ret ? -EINVAL : 0; } @@ -1004,26 +1005,11 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) if (var->xres < 8 || var->yres < 8) return -EINVAL; - /* Too huge resolution causes multiplication overflow. */ - if (check_mul_overflow(var->xres, var->yres, &unused) || - check_mul_overflow(var->xres_virtual, var->yres_virtual, &unused)) - return -EINVAL; - ret = info->fbops->fb_check_var(var, info); if (ret) return ret; - /* verify that virtual resolution >= physical resolution */ - if (var->xres_virtual < var->xres || - var->yres_virtual < var->yres) { - pr_warn("WARNING: fbcon: Driver '%s' missed to adjust virtual screen size (%ux%u vs. %ux%u)\n", - info->fix.id, - var->xres_virtual, var->yres_virtual, - var->xres, var->yres); - return -EINVAL; - } - if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) return 0; @@ -1114,9 +1100,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, return -EFAULT; console_lock(); lock_fb_info(info); - ret = fbcon_modechange_possible(info, &var); - if (!ret) - ret = fb_set_var(info, &var); + ret = fb_set_var(info, &var); if (!ret) fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL); unlock_fb_info(info); @@ -1176,17 +1160,13 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, console_unlock(); break; default: - #ifndef CONFIG_AMLOGIC_MODIFY lock_fb_info(info); - #endif fb = info->fbops; if (fb->fb_ioctl) ret = fb->fb_ioctl(info, cmd, arg); else ret = -ENOTTY; - #ifndef CONFIG_AMLOGIC_MODIFY unlock_fb_info(info); - #endif } return ret; }
diff --git a/drivers/video/fbdev/gbefb.c b/drivers/video/fbdev/gbefb.c index 6fdc6ab..b9f6a82 100644 --- a/drivers/video/fbdev/gbefb.c +++ b/drivers/video/fbdev/gbefb.c
@@ -1269,7 +1269,7 @@ static struct platform_device *gbefb_device; static int __init gbefb_init(void) { int ret = platform_driver_register(&gbefb_driver); - if (IS_ENABLED(CONFIG_SGI_IP32) && !ret) { + if (!ret) { gbefb_device = platform_device_alloc("gbefb", 0); if (gbefb_device) { ret = platform_device_add(gbefb_device);
diff --git a/drivers/video/fbdev/hgafb.c b/drivers/video/fbdev/hgafb.c index 03c0b1b..59e1cae 100644 --- a/drivers/video/fbdev/hgafb.c +++ b/drivers/video/fbdev/hgafb.c
@@ -286,7 +286,7 @@ static int hga_card_detect(void) hga_vram = ioremap(0xb0000, hga_vram_len); if (!hga_vram) - return -ENOMEM; + goto error; if (request_region(0x3b0, 12, "hgafb")) release_io_ports = 1; @@ -346,18 +346,13 @@ static int hga_card_detect(void) hga_type_name = "Hercules"; break; } - return 0; + return 1; error: if (release_io_ports) release_region(0x3b0, 12); if (release_io_port) release_region(0x3bf, 1); - - iounmap(hga_vram); - - pr_err("hgafb: HGA card not detected.\n"); - - return -EINVAL; + return 0; } /** @@ -555,11 +550,13 @@ static struct fb_ops hgafb_ops = { static int hgafb_probe(struct platform_device *pdev) { struct fb_info *info; - int ret; - ret = hga_card_detect(); - if (ret) - return ret; + if (! hga_card_detect()) { + printk(KERN_INFO "hgafb: HGA card not detected.\n"); + if (hga_vram) + iounmap(hga_vram); + return -EINVAL; + } printk(KERN_INFO "hgafb: %s with %ldK of memory detected.\n", hga_type_name, hga_vram_len/1024);
diff --git a/drivers/video/fbdev/imsttfb.c b/drivers/video/fbdev/imsttfb.c index 5f610d4..58b01c7 100644 --- a/drivers/video/fbdev/imsttfb.c +++ b/drivers/video/fbdev/imsttfb.c
@@ -1512,6 +1512,11 @@ static int imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) info->fix.smem_start = addr; info->screen_base = (__u8 *)ioremap(addr, par->ramdac == IBM ? 0x400000 : 0x800000); + if (!info->screen_base) { + release_mem_region(addr, size); + framebuffer_release(info); + return -ENOMEM; + } info->fix.mmio_start = addr + 0x800000; par->dc_regs = ioremap(addr + 0x800000, 0x1000); par->cmap_regs_phys = addr + 0x840000;
diff --git a/drivers/video/fbdev/imxfb.c b/drivers/video/fbdev/imxfb.c index ffde310..b3286d1 100644 --- a/drivers/video/fbdev/imxfb.c +++ b/drivers/video/fbdev/imxfb.c
@@ -977,7 +977,7 @@ static int imxfb_probe(struct platform_device *pdev) 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"); + dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret); ret = -ENOMEM; goto failed_map; }
diff --git a/drivers/video/fbdev/kyro/fbdev.c b/drivers/video/fbdev/kyro/fbdev.c index 74bf26b..a7bd9f2 100644 --- a/drivers/video/fbdev/kyro/fbdev.c +++ b/drivers/video/fbdev/kyro/fbdev.c
@@ -372,11 +372,6 @@ static int kyro_dev_overlay_viewport_set(u32 x, u32 y, u32 ulWidth, u32 ulHeight /* probably haven't called CreateOverlay yet */ return -EINVAL; - if (ulWidth == 0 || ulWidth == 0xffffffff || - ulHeight == 0 || ulHeight == 0xffffffff || - (x < 2 && ulWidth + 2 == 0)) - return -EINVAL; - /* Stop Ramdac Output */ DisableRamdacOutput(deviceInfo.pSTGReg); @@ -399,9 +394,6 @@ static int kyrofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct kyrofb_info *par = info->par; - if (!var->pixclock) - return -EINVAL; - if (var->bits_per_pixel != 16 && var->bits_per_pixel != 32) { printk(KERN_WARNING "kyrofb: depth not supported: %u\n", var->bits_per_pixel); return -EINVAL;
diff --git a/drivers/video/fbdev/nvidia/nv_i2c.c b/drivers/video/fbdev/nvidia/nv_i2c.c index 0b48965..d7994a1 100644 --- a/drivers/video/fbdev/nvidia/nv_i2c.c +++ b/drivers/video/fbdev/nvidia/nv_i2c.c
@@ -86,7 +86,7 @@ static int nvidia_setup_i2c_bus(struct nvidia_i2c_chan *chan, const char *name, { int rc; - strscpy(chan->adapter.name, name, sizeof(chan->adapter.name)); + strcpy(chan->adapter.name, name); chan->adapter.owner = THIS_MODULE; chan->adapter.class = i2c_class; chan->adapter.algo_data = &chan->algo;
diff --git a/drivers/video/fbdev/omap2/omapfb/displays/connector-dvi.c b/drivers/video/fbdev/omap2/omapfb/displays/connector-dvi.c index 777f6d6..b4a1aef 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/connector-dvi.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/connector-dvi.c
@@ -251,7 +251,6 @@ static int dvic_probe_of(struct platform_device *pdev) adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0); if (adapter_node) { adapter = of_get_i2c_adapter_by_node(adapter_node); - of_node_put(adapter_node); if (adapter == NULL) { dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n"); omap_dss_put_device(ddata->in);
diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c index a2c7c5c..4b0793a 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
@@ -409,7 +409,7 @@ static ssize_t dsicm_num_errors_show(struct device *dev, if (r) return r; - return sysfs_emit(buf, "%d\n", errors); + return snprintf(buf, PAGE_SIZE, "%d\n", errors); } static ssize_t dsicm_hw_revision_show(struct device *dev, @@ -439,7 +439,7 @@ static ssize_t dsicm_hw_revision_show(struct device *dev, if (r) return r; - return sysfs_emit(buf, "%02x.%02x.%02x\n", id1, id2, id3); + return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); } static ssize_t dsicm_store_ulps(struct device *dev, @@ -487,7 +487,7 @@ static ssize_t dsicm_show_ulps(struct device *dev, t = ddata->ulps_enabled; mutex_unlock(&ddata->lock); - return sysfs_emit(buf, "%u\n", t); + return snprintf(buf, PAGE_SIZE, "%u\n", t); } static ssize_t dsicm_store_ulps_timeout(struct device *dev, @@ -532,7 +532,7 @@ static ssize_t dsicm_show_ulps_timeout(struct device *dev, t = ddata->ulps_timeout; mutex_unlock(&ddata->lock); - return sysfs_emit(buf, "%u\n", t); + return snprintf(buf, PAGE_SIZE, "%u\n", t); } static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL);
diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-sony-acx565akm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-sony-acx565akm.c index 0cbc5b9..1293515 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/panel-sony-acx565akm.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-sony-acx565akm.c
@@ -476,7 +476,7 @@ static ssize_t show_cabc_available_modes(struct device *dev, int i; if (!ddata->has_cabc) - return sysfs_emit(buf, "%s\n", cabc_modes[0]); + return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]); for (i = 0, len = 0; len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td043mtea1.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td043mtea1.c index 9f6ef9e..bb85b21 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-tpo-td043mtea1.c
@@ -169,7 +169,7 @@ static ssize_t tpo_td043_vmirror_show(struct device *dev, { struct panel_drv_data *ddata = dev_get_drvdata(dev); - return sysfs_emit(buf, "%d\n", ddata->vmirror); + return snprintf(buf, PAGE_SIZE, "%d\n", ddata->vmirror); } static ssize_t tpo_td043_vmirror_store(struct device *dev, @@ -199,7 +199,7 @@ static ssize_t tpo_td043_mode_show(struct device *dev, { struct panel_drv_data *ddata = dev_get_drvdata(dev); - return sysfs_emit(buf, "%d\n", ddata->mode); + return snprintf(buf, PAGE_SIZE, "%d\n", ddata->mode); } static ssize_t tpo_td043_mode_store(struct device *dev,
diff --git a/drivers/video/fbdev/pxa3xx-gcu.c b/drivers/video/fbdev/pxa3xx-gcu.c index 7c4694d..74ffb44 100644 --- a/drivers/video/fbdev/pxa3xx-gcu.c +++ b/drivers/video/fbdev/pxa3xx-gcu.c
@@ -651,7 +651,6 @@ static int pxa3xx_gcu_probe(struct platform_device *pdev) for (i = 0; i < 8; i++) { ret = pxa3xx_gcu_add_buffer(dev, priv); if (ret) { - pxa3xx_gcu_free_buffers(dev, priv); dev_err(dev, "failed to allocate DMA memory\n"); goto err_disable_clk; } @@ -668,15 +667,15 @@ static int pxa3xx_gcu_probe(struct platform_device *pdev) SHARED_SIZE, irq); return 0; -err_disable_clk: - clk_disable_unprepare(priv->clk); +err_free_dma: + dma_free_coherent(dev, SHARED_SIZE, + priv->shared, priv->shared_phys); err_misc_deregister: misc_deregister(&priv->misc_dev); -err_free_dma: - dma_free_coherent(dev, SHARED_SIZE, - priv->shared, priv->shared_phys); +err_disable_clk: + clk_disable_unprepare(priv->clk); return ret; } @@ -689,7 +688,6 @@ static int pxa3xx_gcu_remove(struct platform_device *pdev) pxa3xx_gcu_wait_idle(priv); misc_deregister(&priv->misc_dev); dma_free_coherent(dev, SHARED_SIZE, priv->shared, priv->shared_phys); - clk_disable_unprepare(priv->clk); pxa3xx_gcu_free_buffers(dev, priv); return 0;
diff --git a/drivers/video/fbdev/qti/Kconfig b/drivers/video/fbdev/qti/Kconfig new file mode 100644 index 0000000..a683816 --- /dev/null +++ b/drivers/video/fbdev/qti/Kconfig
@@ -0,0 +1,24 @@ +config FB_QTI_QPIC + tristate "QPIC Framebuffer support" + depends on FB && ARCH_QCOM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + ---help--- + Support for QTI QPIC framebuffer. + QPIC is a wrapper module provides support for LCD controller + connected with parallel interfaces pins. This option provides + the support for QPIC framebuffer which can be used for display + operations for QPIC LCD. + +config FB_QTI_QPIC_ER_SSD1963_PANEL + tristate "Qpic MIPI ER ssd1963 Panel" + depends on FB_QTI_QPIC + ---help--- + Support for MIPI ER ssd1963 (800x480) panel + EAST Rising TECHNOLOGY ssd1963 + with on-chip full display RAM + use 9-bit parallel interface +
diff --git a/drivers/video/fbdev/qti/Makefile b/drivers/video/fbdev/qti/Makefile new file mode 100644 index 0000000..8ba73e9 --- /dev/null +++ b/drivers/video/fbdev/qti/Makefile
@@ -0,0 +1,7 @@ +ccflags-y += -I$(src) +ccflags-y += -I$(srctree)/arch/arm/mach-qcom/include +ccflags-y += -I$(srctree)/arch/arm/mach-qcom + +mdss-qpic-objs := mdss_qpic.o mdss_fb.o mdss_qpic_panel.o +obj-$(CONFIG_FB_QTI_QPIC) += mdss-qpic.o +obj-$(CONFIG_FB_QTI_QPIC_ER_SSD1963_PANEL) += qpic_panel_er_ssd1963.o
diff --git a/drivers/video/fbdev/qti/mdss_fb.c b/drivers/video/fbdev/qti/mdss_fb.c new file mode 100644 index 0000000..3721e07 --- /dev/null +++ b/drivers/video/fbdev/qti/mdss_fb.c
@@ -0,0 +1,1142 @@ +/* + * Core MDSS framebuffer driver. + * + * Copyright (C) 2007 Google Incorporated + * Copyright (c) 2008-2014, 2020 The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/string.h> +#include "mdss_fb.h" + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MDSS_FB_NUM 3 +#else +#define MDSS_FB_NUM 2 +#endif + +#ifndef EXPORT_COMPAT +#define EXPORT_COMPAT(x) +#endif + +#define MAX_FBI_LIST 32 + +#define BLANK_FLAG_LP FB_BLANK_VSYNC_SUSPEND +#define BLANK_FLAG_ULP FB_BLANK_NORMAL + +#define DEFERRED_IO_REFRESH_RATE 25 +#define DEFERRED_IO_DELAY (HZ / DEFERRED_IO_REFRESH_RATE) + +enum { + MDP_RGB_565, /* RGB 565 planer */ + MDP_XRGB_8888, /* RGB 888 padded */ + MDP_ARGB_8888, /* ARGB 888 */ + MDP_RGB_888, /* RGB 888 planer */ + MDP_RGBA_8888, /* ARGB 888 */ + MDP_BGRA_8888, /* ABGR 888 */ + MDP_BGR_565, /* BGR 565 planer */ +}; + +static struct fb_info *fbi_list[MAX_FBI_LIST]; +static int fbi_list_index; + +static u32 mdss_fb_pseudo_palette[16] = { + 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + +static struct msm_mdp_interface *mdp_instance; +static struct fb_deferred_io mdss_fb_defio; + +static int mdss_fb_register(struct msm_fb_data_type *mfd); +static int mdss_fb_open(struct fb_info *info, int user); +static int mdss_fb_release(struct fb_info *info, int user); +static int mdss_fb_release_all(struct fb_info *info, bool release_all); +static int mdss_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); +static int mdss_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); +static int mdss_fb_set_par(struct fb_info *info); +static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, + int op_enable); +static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd); + +static void mdss_fb_shutdown(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + + lock_fb_info(mfd->fbi); + mdss_fb_release_all(mfd->fbi, true); + unlock_fb_info(mfd->fbi); +} + +static int mdss_fb_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = NULL; + struct mdss_panel_data *pdata; + struct fb_info *fbi; + int rc; + + if (fbi_list_index >= MAX_FBI_LIST) + return -ENOMEM; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + return -EPROBE_DEFER; + + /* + * alloc framebuffer info + par data + */ + fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL); + if (fbi == NULL) { + pr_err("can't allocate framebuffer info data!\n"); + return -ENOMEM; + } + + mfd = (struct msm_fb_data_type *)fbi->par; + mfd->key = MFD_KEY; + mfd->fbi = fbi; + mfd->panel_info = &pdata->panel_info; + mfd->panel.type = pdata->panel_info.type; + mfd->panel.id = mfd->index; + mfd->fb_page = MDSS_FB_NUM; + mfd->index = fbi_list_index; + + mfd->fb_imgType = MDP_RGBA_8888; + + mfd->fbi->fbdefio = &mdss_fb_defio; + mfd->pdev = pdev; + + mfd->mdp = *mdp_instance; + + fbi_list[fbi_list_index++] = fbi; + + platform_set_drvdata(pdev, mfd); + + rc = mdss_fb_register(mfd); + if (rc) + return rc; + + if (mfd->mdp.init_fnc) { + rc = mfd->mdp.init_fnc(mfd); + if (rc) { + pr_err("init_fnc failed\n"); + return rc; + } + } + + rc = pm_runtime_set_active(mfd->fbi->dev); + if (rc < 0) + pr_err("pm_runtime: fail to set active.\n"); + pm_runtime_enable(mfd->fbi->dev); + + return rc; +} + +static int mdss_fb_remove(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if (!mfd) + return -ENODEV; + + pm_runtime_disable(mfd->fbi->dev); + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (mdss_fb_suspend_sub(mfd)) + pr_err("msm_fb_remove: can't stop the device %d\n", + mfd->index); + + fb_deferred_io_cleanup(mfd->fbi); + /* remove /dev/fb* */ + unregister_framebuffer(mfd->fbi); + + return 0; +} + +static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd) +{ + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + pr_debug("mdss_fb suspend index=%d\n", mfd->index); + + mfd->suspend.op_enable = mfd->op_enable; + mfd->suspend.panel_power_state = mfd->panel_power_state; + + if (mfd->op_enable) { + /* + * Ideally, display should have either been blanked by now, or + * should have transitioned to a low power state. If not, then + * as a fall back option, enter ulp state to leave the display + * on, but turn off all interface clocks. + */ + if (mdss_fb_is_power_on(mfd)) { + ret = mdss_fb_blank_sub(BLANK_FLAG_ULP, mfd->fbi, + mfd->suspend.op_enable); + if (ret) { + pr_err("can't turn off display!\n"); + return ret; + } + } + mfd->op_enable = false; + fb_set_suspend(mfd->fbi, FBINFO_STATE_SUSPENDED); + } + + return 0; +} + +static int mdss_fb_resume_sub(struct msm_fb_data_type *mfd) +{ + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + pr_debug("mdss_fb resume index=%d\n", mfd->index); + + /* resume state var recover */ + mfd->op_enable = mfd->suspend.op_enable; + + /* + * If the fb was explicitly blanked or transitioned to ulp during + * suspend, then undo it during resume with the appropriate unblank + * flag. If fb was in ulp state when entering suspend, then nothing + * needs to be done. + */ + if (mdss_panel_is_power_on(mfd->suspend.panel_power_state) && + !mdss_panel_is_power_on_ulp(mfd->suspend.panel_power_state)) { + int unblank_flag = mdss_panel_is_power_on_interactive( + mfd->suspend.panel_power_state) ? FB_BLANK_UNBLANK : + BLANK_FLAG_LP; + + ret = mdss_fb_blank_sub(unblank_flag, mfd->fbi, mfd->op_enable); + if (ret) + pr_warn("can't turn on display!\n"); + else + fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING); + } + + return ret; +} + +#if defined(CONFIG_PM) && !defined(CONFIG_PM_SLEEP) +static int mdss_fb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + if (!mfd) + return -ENODEV; + + dev_dbg(&pdev->dev, "display suspend\n"); + + return mdss_fb_suspend_sub(mfd); +} + +static int mdss_fb_resume(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + if (!mfd) + return -ENODEV; + + dev_dbg(&pdev->dev, "display resume\n"); + + return mdss_fb_resume_sub(mfd); +} +#else +#define mdss_fb_suspend NULL +#define mdss_fb_resume NULL +#endif + +#ifdef CONFIG_PM_SLEEP +static int mdss_fb_pm_suspend(struct device *dev) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + + if (!mfd) + return -ENODEV; + + dev_dbg(dev, "display pm suspend\n"); + + return mdss_fb_suspend_sub(mfd); +} + +static int mdss_fb_pm_resume(struct device *dev) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + if (!mfd) + return -ENODEV; + + dev_dbg(dev, "display pm resume\n"); + + /* + * It is possible that the runtime status of the fb device may + * have been active when the system was suspended. Reset the runtime + * status to suspended state after a complete system resume. + */ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + + return mdss_fb_resume_sub(mfd); +} +#endif + +static const struct dev_pm_ops mdss_fb_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mdss_fb_pm_suspend, mdss_fb_pm_resume) +}; + +static const struct of_device_id mdss_fb_dt_match[] = { + { .compatible = "qti,mdss-fb",}, + {} +}; +EXPORT_COMPAT("qti,mdss-fb"); + +static struct platform_driver mdss_fb_driver = { + .probe = mdss_fb_probe, + .remove = mdss_fb_remove, + .suspend = mdss_fb_suspend, + .resume = mdss_fb_resume, + .shutdown = mdss_fb_shutdown, + .driver = { + .name = "mdss_fb", + .of_match_table = mdss_fb_dt_match, + .pm = &mdss_fb_pm_ops, + }, +}; + +static int mdss_fb_blank_blank(struct msm_fb_data_type *mfd, + int req_power_state) +{ + int ret = 0; + int cur_power_state; + + if (!mfd) + return -EINVAL; + + if (!mdss_fb_is_power_on(mfd) || !mfd->mdp.off_fnc) + return 0; + + cur_power_state = mfd->panel_power_state; + + pr_debug("Transitioning from %d --> %d\n", cur_power_state, + req_power_state); + + if (cur_power_state == req_power_state) { + pr_debug("No change in power state\n"); + return 0; + } + + mfd->op_enable = false; + mfd->panel_power_state = req_power_state; + + ret = mfd->mdp.off_fnc(mfd); + if (ret) + mfd->panel_power_state = cur_power_state; + mfd->op_enable = true; + + return ret; +} + +static int mdss_fb_blank_unblank(struct msm_fb_data_type *mfd) +{ + int ret = 0; + int cur_power_state; + + if (!mfd) + return -EINVAL; + + cur_power_state = mfd->panel_power_state; + pr_debug("Transitioning from %d --> %d\n", cur_power_state, + MDSS_PANEL_POWER_ON); + + if (mdss_panel_is_power_on_interactive(cur_power_state)) { + pr_debug("No change in power state\n"); + return 0; + } + + if (mfd->mdp.on_fnc) { + ret = mfd->mdp.on_fnc(mfd); + if (ret) + goto error; + + mfd->panel_power_state = MDSS_PANEL_POWER_ON; + mfd->panel_info->panel_dead = false; + + } + +error: + return ret; +} + +static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, + int op_enable) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret = 0; + int cur_power_state, req_power_state = MDSS_PANEL_POWER_OFF; + + if (!mfd || !op_enable) + return -EPERM; + + pr_debug("%pS mode:%d\n", __builtin_return_address(0), + blank_mode); + + cur_power_state = mfd->panel_power_state; + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + pr_debug("unblank called. cur pwr state=%d\n", cur_power_state); + ret = mdss_fb_blank_unblank(mfd); + break; + case BLANK_FLAG_ULP: + req_power_state = MDSS_PANEL_POWER_LP2; + pr_debug("ultra low power mode requested\n"); + if (mdss_fb_is_power_off(mfd)) { + pr_debug("Unsupp transition: off --> ulp\n"); + return 0; + } + + ret = mdss_fb_blank_blank(mfd, req_power_state); + break; + case BLANK_FLAG_LP: + req_power_state = MDSS_PANEL_POWER_LP1; + pr_debug(" power mode requested\n"); + + /* + * If low power mode is requested when panel is already off, + * then first unblank the panel before entering low power mode + */ + if (mdss_fb_is_power_off(mfd) && mfd->mdp.on_fnc) { + pr_debug("off --> lp. switch to on first\n"); + ret = mdss_fb_blank_unblank(mfd); + if (ret) + break; + } + + ret = mdss_fb_blank_blank(mfd, req_power_state); + break; + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + default: + req_power_state = MDSS_PANEL_POWER_OFF; + pr_debug("blank powerdown called\n"); + ret = mdss_fb_blank_blank(mfd, req_power_state); + break; + } + + return ret; +} + +static void mdss_fb_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + mdss_fb_pan_display(&info->var, info); +} + +static int mdss_fb_blank(int blank_mode, struct fb_info *info) +{ + struct mdss_panel_data *pdata; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (mfd->op_enable == 0) { + if (blank_mode == FB_BLANK_UNBLANK) + mfd->suspend.panel_power_state = MDSS_PANEL_POWER_ON; + else if (blank_mode == BLANK_FLAG_ULP) + mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP2; + else if (blank_mode == BLANK_FLAG_LP) + mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP1; + else + mfd->suspend.panel_power_state = MDSS_PANEL_POWER_OFF; + return 0; + } + pr_debug("mode: %d\n", blank_mode); + + pdata = dev_get_platdata(&mfd->pdev->dev); + + if (pdata->panel_info.is_lpm_mode && + blank_mode == FB_BLANK_UNBLANK) { + pr_debug("panel is in lpm mode\n"); + mfd->mdp.configure_panel(mfd, 0); + pdata->panel_info.is_lpm_mode = false; + } + + return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); +} + +static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + + return dma_mmap_coherent(info->dev, vma, + info->screen_base, + info->fix.smem_start, + info->fix.smem_len); +} + +static struct fb_ops mdss_fb_ops = { + .owner = THIS_MODULE, + .fb_open = mdss_fb_open, + .fb_release = mdss_fb_release, + .fb_check_var = mdss_fb_check_var, /* vinfo check */ + .fb_set_par = mdss_fb_set_par, /* set the video mode */ + .fb_blank = mdss_fb_blank, /* blank display */ + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = mdss_fb_mmap, +}; + +static struct fb_deferred_io mdss_fb_defio = { + .delay = DEFERRED_IO_DELAY, + .deferred_io = mdss_fb_deferred_io, +}; + +static int mdss_fb_alloc_fbmem(struct msm_fb_data_type *mfd) +{ + + if (mfd->mdp.fb_mem_alloc_fnc) { + return mfd->mdp.fb_mem_alloc_fnc(mfd); + } else { + pr_err("no fb memory allocator function defined\n"); + return -ENOMEM; + } +} + +static int mdss_fb_register(struct msm_fb_data_type *mfd) +{ + int ret = -ENODEV; + int bpp; + struct mdss_panel_info *panel_info = mfd->panel_info; + struct fb_info *fbi = mfd->fbi; + struct fb_fix_screeninfo *fix; + struct fb_var_screeninfo *var; + int *id; + + /* + * fb info initialization + */ + fix = &fbi->fix; + var = &fbi->var; + + fix->type_aux = 0; /* if type == FB_TYPE_INTERLEAVED_PLANES */ + fix->visual = FB_VISUAL_TRUECOLOR; /* True Color */ + fix->ywrapstep = 0; /* No support */ + fix->mmio_start = 0; /* No MMIO Address */ + fix->mmio_len = 0; /* No MMIO Address */ + fix->accel = FB_ACCEL_NONE;/* FB_ACCEL_MSM needes to be added in fb.h */ + + var->xoffset = 0, /* Offset from virtual to visible */ + var->yoffset = 0, /* resolution */ + var->grayscale = 0, /* No graylevels */ + var->nonstd = 0, /* standard pixel format */ + var->activate = FB_ACTIVATE_VBL, /* activate it at vsync */ + var->height = -1, /* height of picture in mm */ + var->width = -1, /* width of picture in mm */ + var->accel_flags = 0, /* acceleration flags */ + var->sync = 0, /* see FB_SYNC_* */ + var->rotate = 0, /* angle we rotate counter clockwise */ + mfd->op_enable = false; + + switch (mfd->fb_imgType) { + case MDP_RGB_565: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 11; + var->blue.length = 5; + var->green.length = 6; + var->red.length = 5; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 2; + break; + + case MDP_RGB_888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 3; + break; + + case MDP_ARGB_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 24; + var->green.offset = 16; + var->red.offset = 8; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 8; + bpp = 4; + break; + + case MDP_RGBA_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 16; + var->green.offset = 8; + var->red.offset = 0; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 24; + var->transp.length = 8; + bpp = 4; + break; + + default: + pr_err("msm_fb_init: fb %d unkown image type!\n", + mfd->index); + return ret; + } + + var->xres = panel_info->xres; + + fix->type = panel_info->is_3d_panel; + if (mfd->mdp.fb_stride) + fix->line_length = mfd->mdp.fb_stride(mfd->index, var->xres, + bpp); + else + fix->line_length = var->xres * bpp; + + var->yres = panel_info->yres; + if (panel_info->physical_width) + var->width = panel_info->physical_width; + if (panel_info->physical_height) + var->height = panel_info->physical_height; + var->xres_virtual = var->xres; + var->yres_virtual = panel_info->yres * mfd->fb_page; + var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */ + var->upper_margin = panel_info->lcdc.v_back_porch; + var->lower_margin = panel_info->lcdc.v_front_porch; + var->vsync_len = panel_info->lcdc.v_pulse_width; + var->left_margin = panel_info->lcdc.h_back_porch; + var->right_margin = panel_info->lcdc.h_front_porch; + var->hsync_len = panel_info->lcdc.h_pulse_width; + var->pixclock = panel_info->clk_rate / 1000; + + /* + * Populate smem length here for uspace to get the + * Framebuffer size when FBIO_FSCREENINFO ioctl is + * called. + */ + fix->smem_len = PAGE_ALIGN(fix->line_length * var->yres) * mfd->fb_page; + + /* id field for fb app */ + id = (int *)&mfd->panel; + + snprintf(fix->id, sizeof(fix->id), "mdssfb_%x", (u32) *id); + + fbi->fbops = &mdss_fb_ops; + fbi->flags = FBINFO_FLAG_DEFAULT | FBINFO_DMAFB; + fbi->pseudo_palette = mdss_fb_pseudo_palette; + + mfd->ref_cnt = 0; + mfd->panel_power_state = MDSS_PANEL_POWER_OFF; + + if (mdss_fb_alloc_fbmem(mfd)) + pr_warn("unable to allocate fb memory in fb register\n"); + + mfd->op_enable = true; + + fb_deferred_io_init(mfd->fbi); + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (ret) + pr_err("fb_alloc_cmap() failed!\n"); + + if (register_framebuffer(fbi) < 0) { + fb_dealloc_cmap(&fbi->cmap); + + mfd->op_enable = false; + return -EPERM; + } + + pr_info("FrameBuffer[%d] %dx%d registered successfully!\n", mfd->index, + fbi->var.xres, fbi->var.yres); + + return 0; +} + +static int mdss_fb_open(struct fb_info *info, int user) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int result; + + + result = pm_runtime_get_sync(info->dev); + + if (result < 0) { + pr_err("pm_runtime: fail to wake up\n"); + goto pm_error; + } + + if (!mfd->ref_cnt) { + result = mdss_fb_blank_sub(FB_BLANK_UNBLANK, info, + mfd->op_enable); + if (result) { + pr_err("can't turn on fb%d! rc=%d\n", mfd->index, + result); + goto blank_error; + } + } + + mfd->ref_cnt++; + + return 0; + +blank_error: + pm_runtime_put(info->dev); + +pm_error: + return result; +} + +static int mdss_fb_release_all(struct fb_info *info, bool release_all) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret = 0; + bool release_needed = false; + + if (!mfd->ref_cnt) { + pr_info("try to close unopened fb %d!\n", mfd->index); + return -EINVAL; + } + + pr_debug("release_all = %s\n", release_all ? "true" : "false"); + + mfd->ref_cnt--; + + if (!mfd->ref_cnt || release_all) { + /* resources (if any) will be released during blank */ + if (mfd->mdp.release_fnc) + mfd->mdp.release_fnc(mfd, true); + + ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, + mfd->op_enable); + if (ret) { + pr_err("can't turn off fb%d!\n", ret); + return ret; + } + } else if (release_needed) { + if (mfd->mdp.release_fnc) { + ret = mfd->mdp.release_fnc(mfd, false); + + /* display commit is needed to release resources */ + if (ret) + mdss_fb_pan_display(&mfd->fbi->var, mfd->fbi); + } + } + + return ret; +} + +static int mdss_fb_release(struct fb_info *info, int user) +{ + return mdss_fb_release_all(info, false); +} + +static int mdss_fb_pan_display_sub(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (!mfd->op_enable) + return -EPERM; + + if (var->xoffset > (info->var.xres_virtual - info->var.xres)) + return -EINVAL; + + if (var->yoffset > (info->var.yres_virtual - info->var.yres)) + return -EINVAL; + + if (info->fix.xpanstep) + info->var.xoffset = + (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep; + + if (info->fix.ypanstep) + info->var.yoffset = + (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep; + + if (mfd->mdp.dma_fnc) + mfd->mdp.dma_fnc(mfd); + else + pr_warn("dma function not set for panel type=%d\n", + mfd->panel.type); + + return 0; +} + +static int mdss_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + return mdss_fb_pan_display_sub(var, info); +} + +static void mdss_fb_var_to_panelinfo(struct fb_var_screeninfo *var, + struct mdss_panel_info *pinfo) +{ + pinfo->xres = var->xres; + pinfo->yres = var->yres; + pinfo->lcdc.v_front_porch = var->lower_margin; + pinfo->lcdc.v_back_porch = var->upper_margin; + pinfo->lcdc.v_pulse_width = var->vsync_len; + pinfo->lcdc.h_front_porch = var->right_margin; + pinfo->lcdc.h_back_porch = var->left_margin; + pinfo->lcdc.h_pulse_width = var->hsync_len; + pinfo->clk_rate = var->pixclock; +} + +static int mdss_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (var->rotate != FB_ROTATE_UR) + return -EINVAL; + if (var->grayscale != info->var.grayscale) + return -EINVAL; + + switch (var->bits_per_pixel) { + case 16: + if ((var->green.offset != 5) || + !((var->blue.offset == 11) + || (var->blue.offset == 0)) || + !((var->red.offset == 11) + || (var->red.offset == 0)) || + (var->blue.length != 5) || + (var->green.length != 6) || + (var->red.length != 5) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0) || + (var->transp.offset != 0) || + (var->transp.length != 0)) + return -EINVAL; + break; + + case 24: + if ((var->blue.offset != 0) || + (var->green.offset != 8) || + (var->red.offset != 16) || + (var->blue.length != 8) || + (var->green.length != 8) || + (var->red.length != 8) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0) || + !(((var->transp.offset == 0) && + (var->transp.length == 0)) || + ((var->transp.offset == 24) && + (var->transp.length == 8)))) + return -EINVAL; + break; + + case 32: + /* Check user specified color format BGRA/ARGB/RGBA + and verify the position of the RGB components */ + + if (!((var->transp.offset == 24) && + (var->blue.offset == 0) && + (var->green.offset == 8) && + (var->red.offset == 16)) && + !((var->transp.offset == 24) && + (var->blue.offset == 16) && + (var->green.offset == 8) && + (var->red.offset == 0))) + return -EINVAL; + + /* Check the common values for both RGBA and ARGB */ + + if ((var->blue.length != 8) || + (var->green.length != 8) || + (var->red.length != 8) || + (var->transp.length != 8) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0)) + return -EINVAL; + + break; + + default: + return -EINVAL; + } + + if ((var->xres_virtual <= 0) || (var->yres_virtual <= 0)) + return -EINVAL; + + if (info->fix.smem_start) { + u32 len = var->xres_virtual * var->yres_virtual * + (var->bits_per_pixel / 8); + if (len > info->fix.smem_len) + return -EINVAL; + } + + if ((var->xres == 0) || (var->yres == 0)) + return -EINVAL; + + if (var->xoffset > (var->xres_virtual - var->xres)) + return -EINVAL; + + if (var->yoffset > (var->yres_virtual - var->yres)) + return -EINVAL; + + if (mfd->panel_info) { + struct mdss_panel_info panel_info; + + memcpy(&panel_info, mfd->panel_info, sizeof(panel_info)); + mdss_fb_var_to_panelinfo(var, &panel_info); + mfd->panel_reconfig = true; + } + + return 0; +} + +static int mdss_fb_set_par(struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_var_screeninfo *var = &info->var; + int old_imgType; + + old_imgType = mfd->fb_imgType; + switch (var->bits_per_pixel) { + case 16: + if (var->red.offset == 0) + mfd->fb_imgType = MDP_BGR_565; + else + mfd->fb_imgType = MDP_RGB_565; + break; + + case 24: + if ((var->transp.offset == 0) && (var->transp.length == 0)) + mfd->fb_imgType = MDP_RGB_888; + else if ((var->transp.offset == 24) && + (var->transp.length == 8)) { + mfd->fb_imgType = MDP_ARGB_8888; + info->var.bits_per_pixel = 32; + } + break; + + case 32: + if ((var->red.offset == 0) && + (var->green.offset == 8) && + (var->blue.offset == 16) && + (var->transp.offset == 24)) + mfd->fb_imgType = MDP_RGBA_8888; + else if ((var->red.offset == 16) && + (var->green.offset == 8) && + (var->blue.offset == 0) && + (var->transp.offset == 24)) + mfd->fb_imgType = MDP_BGRA_8888; + else if ((var->red.offset == 8) && + (var->green.offset == 16) && + (var->blue.offset == 24) && + (var->transp.offset == 0)) + mfd->fb_imgType = MDP_ARGB_8888; + else + mfd->fb_imgType = MDP_RGBA_8888; + break; + + default: + return -EINVAL; + } + + + if (mfd->mdp.fb_stride) + mfd->fbi->fix.line_length = mfd->mdp.fb_stride(mfd->index, + var->xres, + var->bits_per_pixel / 8); + else + mfd->fbi->fix.line_length = var->xres * var->bits_per_pixel / 8; + + mfd->fbi->fix.smem_len = PAGE_ALIGN(mfd->fbi->fix.line_length * + mfd->fbi->var.yres) * mfd->fb_page; + + if (mfd->panel_reconfig || (mfd->fb_imgType != old_imgType)) { + mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); + mdss_fb_var_to_panelinfo(var, mfd->panel_info); + mdss_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable); + mfd->panel_reconfig = false; + } + + return 0; +} + +static int mdss_fb_register_extra_panel(struct platform_device *pdev, + struct mdss_panel_data *pdata) +{ + struct mdss_panel_data *fb_pdata; + + fb_pdata = dev_get_platdata(&pdev->dev); + if (!fb_pdata) { + pr_err("framebuffer device %s contains invalid panel data\n", + dev_name(&pdev->dev)); + return -EINVAL; + } + + if (fb_pdata->next) { + pr_err("split panel already setup for framebuffer device %s\n", + dev_name(&pdev->dev)); + return -EEXIST; + } + + fb_pdata->next = pdata; + + return 0; +} + +int mdss_register_panel(struct platform_device *pdev, + struct mdss_panel_data *pdata) +{ + struct platform_device *fb_pdev, *mdss_pdev; + struct device_node *node; + int rc = 0; + bool master_panel = true; + + if (!pdev || !pdev->dev.of_node) { + pr_err("Invalid device node\n"); + return -ENODEV; + } + + if (!mdp_instance) { + pr_err("mdss mdp resource not initialized yet\n"); + return -EPROBE_DEFER; + } + + node = of_parse_phandle(pdev->dev.of_node, "qti,mdss-fb-map", 0); + if (!node) { + pr_err("Unable to find fb node for device: %s\n", + pdev->name); + return -ENODEV; + } + mdss_pdev = of_find_device_by_node(node->parent); + if (!mdss_pdev) { + pr_err("Unable to find mdss for node: %s\n", node->full_name); + rc = -ENODEV; + goto mdss_notfound; + } + + fb_pdev = of_find_device_by_node(node); + if (fb_pdev) { + rc = mdss_fb_register_extra_panel(fb_pdev, pdata); + if (rc == 0) + master_panel = false; + } else { + pr_info("adding framebuffer device %s\n", dev_name(&pdev->dev)); + fb_pdev = of_platform_device_create(node, NULL, + &mdss_pdev->dev); + if (fb_pdev) + fb_pdev->dev.platform_data = pdata; + } + + if (master_panel && mdp_instance->panel_register_done) + mdp_instance->panel_register_done(pdata); + +mdss_notfound: + of_node_put(node); + return rc; +} +EXPORT_SYMBOL(mdss_register_panel); + +int mdss_fb_register_mdp_instance(struct msm_mdp_interface *mdp) +{ + if (mdp_instance) { + pr_err("multiple MDP instance registration\n"); + return -EINVAL; + } + + mdp_instance = mdp; + return 0; +} +EXPORT_SYMBOL(mdss_fb_register_mdp_instance); + +int mdss_fb_get_phys_info(dma_addr_t *start, unsigned long *len, int fb_num) +{ + struct fb_info *info; + struct msm_fb_data_type *mfd; + + if (fb_num >= MAX_FBI_LIST) + return -EINVAL; + + info = fbi_list[fb_num]; + if (!info) + return -ENOENT; + + mfd = (struct msm_fb_data_type *)info->par; + if (!mfd) + return -ENODEV; + + *start = info->fix.smem_start; + *len = info->fix.smem_len; + + return 0; +} +EXPORT_SYMBOL(mdss_fb_get_phys_info); + +int __init mdss_fb_init(void) +{ + int rc = -ENODEV; + + if (platform_driver_register(&mdss_fb_driver)) + return rc; + + return 0; +} + +module_init(mdss_fb_init); +
diff --git a/drivers/video/fbdev/qti/mdss_fb.h b/drivers/video/fbdev/qti/mdss_fb.h new file mode 100644 index 0000000..9ae7c42 --- /dev/null +++ b/drivers/video/fbdev/qti/mdss_fb.h
@@ -0,0 +1,102 @@ +/* Copyright (c) 2008-2014, 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MDSS_FB_H +#define MDSS_FB_H + +#include <linux/fb.h> +#include <linux/types.h> + +#include "mdss_panel.h" + +#define MSM_FB_DEFAULT_PAGE_SIZE 2 +#define MFD_KEY 0x11161126 +#define MSM_FB_MAX_DEV_LIST 32 + +struct disp_info_type_suspend { + int op_enable; + int panel_power_state; +}; +struct msm_fb_data_type; + +struct msm_mdp_interface { + int (*fb_mem_alloc_fnc)(struct msm_fb_data_type *mfd); + int (*init_fnc)(struct msm_fb_data_type *mfd); + int (*on_fnc)(struct msm_fb_data_type *mfd); + int (*off_fnc)(struct msm_fb_data_type *mfd); + /* called to release resources associated to the process */ + int (*release_fnc)(struct msm_fb_data_type *mfd, bool release_all); + int (*ioctl_handler)(struct msm_fb_data_type *mfd, u32 cmd, void *arg); + void (*dma_fnc)(struct msm_fb_data_type *mfd); + int (*cursor_update)(struct msm_fb_data_type *mfd, + struct fb_cursor *cursor); + int (*panel_register_done)(struct mdss_panel_data *pdata); + u32 (*fb_stride)(u32 fb_index, u32 xres, int bpp); + int (*configure_panel)(struct msm_fb_data_type *mfd, int mode); + void *private1; +}; + +struct msm_fb_data_type { + u32 key; + u32 index; + u32 ref_cnt; + u32 fb_page; + + struct panel_id panel; + struct mdss_panel_info *panel_info; + + u32 dest; + struct fb_info *fbi; + + int op_enable; + u32 fb_imgType; + int panel_reconfig; + + u32 dst_format; + int panel_power_state; + struct disp_info_type_suspend suspend; + + struct platform_device *pdev; + + struct msm_mdp_interface mdp; +}; + +static inline bool mdss_fb_is_power_off(struct msm_fb_data_type *mfd) +{ + return mdss_panel_is_power_off(mfd->panel_power_state); +} + +static inline bool mdss_fb_is_power_on_interactive( + struct msm_fb_data_type *mfd) +{ + return mdss_panel_is_power_on_interactive(mfd->panel_power_state); +} + +static inline bool mdss_fb_is_power_on(struct msm_fb_data_type *mfd) +{ + return mdss_panel_is_power_on(mfd->panel_power_state); +} + +static inline bool mdss_fb_is_power_on_lp(struct msm_fb_data_type *mfd) +{ + return mdss_panel_is_power_on_lp(mfd->panel_power_state); +} + +int mdss_fb_get_phys_info(dma_addr_t *start, unsigned long *len, int fb_num); +int mdss_fb_register_mdp_instance(struct msm_mdp_interface *mdp); +int mdss_fb_suspres_panel(struct device *dev, void *data); +int mdss_fb_do_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg); +int mdss_fb_compat_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg); +#endif /* MDSS_FB_H */
diff --git a/drivers/video/fbdev/qti/mdss_panel.h b/drivers/video/fbdev/qti/mdss_panel.h new file mode 100644 index 0000000..9a87df5 --- /dev/null +++ b/drivers/video/fbdev/qti/mdss_panel.h
@@ -0,0 +1,219 @@ +/* Copyright (c) 2008-2014, 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MDSS_PANEL_H +#define MDSS_PANEL_H + +#include <linux/platform_device.h> +#include <linux/stringify.h> +#include <linux/types.h> +#include <linux/debugfs.h> + +/* panel id type */ +struct panel_id { + u16 id; + u16 type; +}; + +#define DEFAULT_FRAME_RATE 60 +#define DEFAULT_ROTATOR_FRAME_RATE 120 +#define MDSS_DSI_RST_SEQ_LEN 10 +#define MDSS_MDP_MAX_FETCH 12 + +/* panel type list */ +#define NO_PANEL 0xffff /* No Panel */ +#define EBI2_PANEL 1 /* EBI2 */ + +/* panel class */ +enum { + DISPLAY_LCD = 0, /* lcd = ebi2/mddi */ + DISPLAY_LCDC, /* lcdc */ + DISPLAY_TV, /* TV Out */ + DISPLAY_EXT_MDDI, /* External MDDI */ + DISPLAY_WRITEBACK, +}; + +enum { + DISPLAY_1 = 0, /* attached as first device */ + MAX_PHYS_TARGET_NUM, +}; + +enum { + MDSS_PANEL_POWER_OFF = 0, + MDSS_PANEL_POWER_ON, + MDSS_PANEL_POWER_LP1, + MDSS_PANEL_POWER_LP2, +}; + +enum { + MDSS_PANEL_BLANK_BLANK = 0, + MDSS_PANEL_BLANK_UNBLANK, + MDSS_PANEL_BLANK_LOW_POWER, +}; + +struct lcd_panel_info { + u32 h_back_porch; + u32 h_front_porch; + u32 h_pulse_width; + u32 v_back_porch; + u32 v_front_porch; + u32 v_pulse_width; + u32 border_clr; + u32 underflow_clr; + u32 hsync_skew; + u32 border_top; + u32 border_bottom; + u32 border_left; + u32 border_right; + /* Pad width */ + u32 xres_pad; + /* Pad height */ + u32 yres_pad; +}; +struct mdss_panel_info { + u32 xres; + u32 yres; + u32 physical_width; + u32 physical_height; + u32 bpp; + u32 type; + u32 wait_cycle; + u32 pdest; + u32 brightness_max; + u32 bl_max; + u32 bl_min; + u32 fb_num; + u32 clk_rate; + u32 clk_min; + u32 clk_max; + u32 frame_count; + u32 is_3d_panel; + u32 out_format; + u32 rst_seq[MDSS_DSI_RST_SEQ_LEN]; + u32 rst_seq_len; + u32 vic; /* video identification code */ + int new_fps; + int panel_max_fps; + int panel_max_vtotal; + u32 mode_gpio_state; + u32 xstart_pix_align; + u32 width_pix_align; + u32 ystart_pix_align; + u32 height_pix_align; + u32 min_width; + u32 min_height; + u32 min_fps; + u32 max_fps; + + u32 partial_update_enabled; + u32 dcs_cmd_by_left; + int panel_power_state; + int blank_state; + + uint32_t panel_dead; + u32 panel_orientation; + bool dynamic_switch_pending; + bool is_lpm_mode; + + struct lcd_panel_info lcdc; +}; + +struct mdss_panel_data { + struct mdss_panel_info panel_info; + void (*set_backlight) (struct mdss_panel_data *pdata, u32 bl_level); + unsigned char *mmss_cc_base; + + /** + * event_handler() - callback handler for MDP core events + * @pdata: Pointer refering to the panel struct associated to this + * event. Can be used to retrieve panel info. + * @e: Event being generated, see enum mdss_intf_events + * @arg: Optional argument to pass some info from some events. + * + * Used to register handler to be used to propagate different events + * happening in MDP core driver. Panel driver can listen for any of + * these events to perform appropriate actions for panel initialization + * and teardown. + */ + int (*event_handler) (struct mdss_panel_data *pdata, int e, void *arg); + + struct mdss_panel_data *next; +}; + +int mdss_register_panel(struct platform_device *pdev, + struct mdss_panel_data *pdata); + +/* + * mdss_panel_is_power_off: - checks if a panel is off + * @panel_power_state: enum identifying the power state to be checked + */ +static inline bool mdss_panel_is_power_off(int panel_power_state) +{ + return (panel_power_state == MDSS_PANEL_POWER_OFF); +} + +/** + * mdss_panel_is_power_on_interactive: - checks if a panel is on and interactive + * @panel_power_state: enum identifying the power state to be checked + * + * This function returns true only is the panel is fully interactive and + * opertaing in normal mode. + */ +static inline bool mdss_panel_is_power_on_interactive(int panel_power_state) +{ + return (panel_power_state == MDSS_PANEL_POWER_ON); +} + +/** + * mdss_panel_is_panel_power_on: - checks if a panel is on + * @panel_power_state: enum identifying the power state to be checked + * + * A panel is considered to be on as long as it can accept any commands + * or data. Sometimes it is posible to program the panel to be in a low + * power non-interactive state. This function returns false only if panel + * has explicitly been turned off. + */ +static inline bool mdss_panel_is_power_on(int panel_power_state) +{ + return !mdss_panel_is_power_off(panel_power_state); +} + +/** + * mdss_panel_is_panel_power_on_lp: - checks if a panel is in a low power mode + * @pdata: pointer to the panel struct associated to the panel + * @panel_power_state: enum identifying the power state to be checked + * + * This function returns true if the panel is in an intermediate low power + * state where it is still on but not fully interactive. It may or may not + * accept any commands and display updates. + */ +static inline bool mdss_panel_is_power_on_lp(int panel_power_state) +{ + return !mdss_panel_is_power_off(panel_power_state) && + !mdss_panel_is_power_on_interactive(panel_power_state); +} + +/** + * mdss_panel_is_panel_power_on_ulp: - checks if panel is in ultra low power mode + * @pdata: pointer to the panel struct associated to the panel + * @panel_power_state: enum identifying the power state to be checked + * + * This function returns true if the panel is in a ultra low power + * state where it is still on but cannot recieve any display updates. + */ +static inline bool mdss_panel_is_power_on_ulp(int panel_power_state) +{ + return panel_power_state == MDSS_PANEL_POWER_LP2; +} + +#endif /* MDSS_PANEL_H */
diff --git a/drivers/video/fbdev/qti/mdss_qpic.c b/drivers/video/fbdev/qti/mdss_qpic.c new file mode 100644 index 0000000..f031831 --- /dev/null +++ b/drivers/video/fbdev/qti/mdss_qpic.c
@@ -0,0 +1,742 @@ +/* Copyright (c) 2013-2014, 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/time.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> + +#include "mdss_fb.h" +#include "mdss_qpic.h" +#include "mdss_qpic_panel.h" + +static int mdss_qpic_probe(struct platform_device *pdev); +static int mdss_qpic_remove(struct platform_device *pdev); +static void qpic_interrupt_en(u32 en); + +struct qpic_data_type *qpic_res; + +/* for debugging */ +static u32 use_bam = true; +static u32 use_irq = true; +static u32 use_vsync = true; + +static const struct of_device_id mdss_qpic_dt_match[] = { + { .compatible = "qti,mdss_qpic",}, + {} +}; +MODULE_DEVICE_TABLE(of, mdss_qpic_dt_match); + +static struct platform_driver mdss_qpic_driver = { + .probe = mdss_qpic_probe, + .remove = mdss_qpic_remove, + .shutdown = NULL, + .driver = { + /* + * Simulate mdp hw + */ + .name = "mdp", + .of_match_table = mdss_qpic_dt_match, + }, +}; + +int qpic_on(struct msm_fb_data_type *mfd) +{ + int ret; + ret = mdss_qpic_panel_on(qpic_res->panel_data, &qpic_res->panel_io); + return ret; +} + +int qpic_off(struct msm_fb_data_type *mfd) +{ + int ret; + ret = mdss_qpic_panel_off(qpic_res->panel_data, &qpic_res->panel_io); + if (use_irq) + qpic_interrupt_en(false); + return ret; +} + + +static void mdss_qpic_pan_display(struct msm_fb_data_type *mfd) +{ + + struct fb_info *fbi; + u32 offset, fb_offset, size, data; + int bpp; + + if (!mfd) { + pr_err("%s: mfd is NULL!", __func__); + return; + } + + fbi = mfd->fbi; + + bpp = fbi->var.bits_per_pixel / 8; + offset = fbi->var.xoffset * bpp + + fbi->var.yoffset * fbi->fix.line_length; + + if (offset > fbi->fix.smem_len) { + pr_err("invalid fb offset=%u total length=%u\n", + (unsigned int)offset, fbi->fix.smem_len); + return; + } + if (use_bam) + fb_offset = (u32)fbi->fix.smem_start + offset; + else + fb_offset = (u32)(uintptr_t)(mfd->fbi->screen_base + offset); + + mdss_qpic_panel_on(qpic_res->panel_data, &qpic_res->panel_io); + size = fbi->var.xres * fbi->var.yres * bpp; + + if (size < (1024 * 1024)) { + qpic_send_frame(0, 0, fbi->var.xres - 1, fbi->var.yres - 1, + (u32 *)((uintptr_t)fb_offset), size); + } else { + /* LCDC BAM can't trnasfer more than 1MB */ + qpic_send_frame(0, 0, fbi->var.xres - 1, fbi->var.yres - 1, + (u32 *)((uintptr_t)fb_offset), size / 2); + + /* Disable vsync interrupt for second packet */ + if (use_vsync) { + data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL); + data &= ~(1 << 0); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data); + } + + qpic_send_frame(0, fbi->var.yres / 2, fbi->var.xres - 1, + fbi->var.yres - 1, (u32 *)((uintptr_t)(fb_offset + +(size / 2))), size / 2); + + /* Enable vsync interrupt after sending second packet */ + if (use_vsync) { + data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL); + data |= (1 << 0); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data); + } + } +} + +static void qpic_bam_cb(void *param) +{ + complete(&qpic_res->completion); +} + +void mdss_qpic_dma_pages_ops(dma_addr_t base, size_t size, + void page_ops(struct page *page)) +{ + dma_addr_t cur; + struct page *page; + + for (cur = base; cur < base + size; cur += PAGE_SIZE) { + page = pfn_to_page(cur >> PAGE_SHIFT); + page_ops(page); + } +} + +int mdss_qpic_alloc_fb_mem(struct msm_fb_data_type *mfd) +{ + size_t size; + u32 yres = mfd->fbi->var.yres_virtual; + + size = PAGE_ALIGN(mfd->fbi->fix.line_length * yres); + + if (!qpic_res->res_init) + return -EINVAL; + + if (mfd->index != 0) { + mfd->fbi->fix.smem_start = 0; + mfd->fbi->screen_base = NULL; + mfd->fbi->fix.smem_len = 0; + return 0; + } + + if (!qpic_res->cmd_buf_virt) { + qpic_res->cmd_buf_virt = dma_alloc_wc( + &qpic_res->pdev->dev, QPIC_MAX_CMD_BUF_SIZE, + (dma_addr_t *)&qpic_res->cmd_buf_phys, GFP_KERNEL); + if (!qpic_res->cmd_buf_virt) { + pr_err("%s cmd buf allocation failed", __func__); + return -ENOMEM; + } + + pr_debug("%s cmd_buf virt=%p phys=%x", __func__, + qpic_res->cmd_buf_virt, + qpic_res->cmd_buf_phys); + } + + if (!qpic_res->fb_virt) { + qpic_res->fb_virt = (void *)dmam_alloc_coherent( + &qpic_res->pdev->dev, + size, + (dma_addr_t *)&qpic_res->fb_phys, + GFP_KERNEL); + if (!qpic_res->fb_virt) { + pr_err("%s fb allocation failed", __func__); + dma_free_wc(&qpic_res->pdev->dev, + QPIC_MAX_CMD_BUF_SIZE, + qpic_res->cmd_buf_virt, + qpic_res->cmd_buf_phys); + qpic_res->cmd_buf_virt = NULL; + return -ENOMEM; + } + + pr_debug("%s size=%d vir_addr=%p phys_addr=0x%x", + __func__, (int)size, qpic_res->fb_virt, + qpic_res->fb_phys); + + qpic_res->fb_size = size; + mdss_qpic_dma_pages_ops(qpic_res->fb_phys, size, get_page); + } + + mfd->fbi->fix.smem_start = qpic_res->fb_phys; + mfd->fbi->screen_base = qpic_res->fb_virt; + mfd->fbi->fix.smem_len = size; + + return 0; +} + +u32 mdss_qpic_fb_stride(u32 fb_index, u32 xres, int bpp) +{ + return xres * bpp; +} + +int mdss_qpic_overlay_init(struct msm_fb_data_type *mfd) +{ + struct msm_mdp_interface *qpic_interface = &mfd->mdp; + qpic_interface->on_fnc = qpic_on; + qpic_interface->off_fnc = qpic_off; + qpic_interface->cursor_update = NULL; + qpic_interface->dma_fnc = mdss_qpic_pan_display; + qpic_interface->ioctl_handler = NULL; + return 0; +} + +int qpic_register_panel(struct mdss_panel_data *pdata) +{ + struct platform_device *mdss_fb_dev = NULL; + int rc; + + mdss_fb_dev = platform_device_alloc("mdss_fb", pdata->panel_info.pdest); + if (!mdss_fb_dev) { + pr_err("unable to allocate mdss_fb device\n"); + return -ENOMEM; + } + + mdss_fb_dev->dev.platform_data = pdata; + + rc = platform_device_add(mdss_fb_dev); + if (rc) { + platform_device_put(mdss_fb_dev); + pr_err("unable to probe mdss_fb device (%d)\n", rc); + return rc; + } + + qpic_res->panel_data = pdata; + + return rc; +} + +void mdss_qpic_set_cfg0(void) +{ + /* + * RD_CS_HOLD=1,RD_ACTIVE=8,CS_WR_RD_SETUP=0, + * WR_CS_HOLD,WR_ACTIVE,ADDR_CS_SETUP=0, + * CMD_BUS_ALIGNMENT=0. + */ + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG0, 0x2000101); +} + +void mdss_qpic_reset(void) +{ + u32 time_end; + + QPIC_OUTP(QPIC_REG_QPIC_LCDC_RESET, 1 << 0); + /* wait 100 us after reset as suggested by hw */ + usleep_range(100, 100); + time_end = (u32)ktime_to_ms(ktime_get()) + + QPIC_MAX_VSYNC_WAIT_TIME; + while (((QPIC_INP(QPIC_REG_QPIC_LCDC_STTS) & (1 << 8)) == 0)) { + if ((u32)ktime_to_ms(ktime_get()) > time_end) { + pr_err("%s reset not finished", __func__); + break; + } + /* yield 100 us for next polling by experiment*/ + usleep_range(100, 100); + } +} + +static void qpic_interrupt_en(u32 en) +{ + QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff); + if (en) { + if (!qpic_res->irq_ena) { + init_completion(&qpic_res->fifo_eof_comp); + qpic_res->irq_ena = true; + enable_irq(qpic_res->irq); + } + } else { + QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0); + disable_irq(qpic_res->irq); + qpic_res->irq_ena = false; + } +} + +static irqreturn_t qpic_irq_handler(int irq, void *ptr) +{ + u32 data; + data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0); + + if (data & ((1 << 2) | (1 << 4))) + complete(&qpic_res->fifo_eof_comp); + return IRQ_HANDLED; +} + +static int qpic_send_pkt_bam(u32 cmd, u32 len, u8 *param) +{ + int ret = 0; + struct dma_async_tx_descriptor *dma_desc; + dma_cookie_t cookie = 0; + + u32 cfg2; + dma_addr_t phys_addr; + + if ((cmd != OP_WRITE_MEMORY_START) && + (cmd != OP_WRITE_MEMORY_CONTINUE)) { + memcpy((u8 *)qpic_res->cmd_buf_virt, param, len); + phys_addr = qpic_res->cmd_buf_phys; + } else { + phys_addr = (dma_addr_t)param; + } + cfg2 = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2); + cfg2 &= ~0xFF; + cfg2 |= cmd; + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2); + + dma_desc = dmaengine_prep_slave_single(qpic_res->chan, phys_addr, len, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); + if (!dma_desc) { + pr_err("failed to prepare dma desc for command %x\n", + cmd); + return -EINVAL; + } + + dma_desc->callback = qpic_bam_cb; + dma_desc->callback_param = qpic_res; + + cookie = dmaengine_submit(dma_desc); + if (dma_submit_error(cookie)) { + pr_err("failed to submit command %x\n", + cmd); + ret = -EINVAL; + } + + reinit_completion(&qpic_res->completion); + dma_async_issue_pending(qpic_res->chan); + + ret = wait_for_completion_timeout( + &qpic_res->completion, + qpic_res->bam_timeout); + + if (ret <= 0) + pr_err("%s timeout %x", __func__, ret); + else + ret = 0; + + return ret; +} + +void qpic_dump_reg(void) +{ + pr_info("%s\n", __func__); + pr_info("QPIC_REG_QPIC_LCDC_CTRL = %x\n", + QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL)); + pr_info("QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT = %x\n", + QPIC_INP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT)); + pr_info("QPIC_REG_QPIC_LCDC_CFG0 = %x\n", + QPIC_INP(QPIC_REG_QPIC_LCDC_CFG0)); + pr_info("QPIC_REG_QPIC_LCDC_CFG1 = %x\n", + QPIC_INP(QPIC_REG_QPIC_LCDC_CFG1)); + pr_info("QPIC_REG_QPIC_LCDC_CFG2 = %x\n", + QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2)); + pr_info("QPIC_REG_QPIC_LCDC_IRQ_EN = %x\n", + QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_EN)); + pr_info("QPIC_REG_QPIC_LCDC_IRQ_STTS = %x\n", + QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS)); + pr_info("QPIC_REG_QPIC_LCDC_STTS = %x\n", + QPIC_INP(QPIC_REG_QPIC_LCDC_STTS)); + pr_info("QPIC_REG_QPIC_LCDC_FIFO_SOF = %x\n", + QPIC_INP(QPIC_REG_QPIC_LCDC_FIFO_SOF)); +} + +static int qpic_wait_for_fifo(void) +{ + u32 data, time_end; + int ret = 0; + + if (use_irq) { + data = QPIC_INP(QPIC_REG_QPIC_LCDC_STTS); + data &= 0x3F; + if (data == 0) + return ret; + reinit_completion(&qpic_res->fifo_eof_comp); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, (1 << 4)); + ret = wait_for_completion_timeout(&qpic_res->fifo_eof_comp, + msecs_to_jiffies(QPIC_MAX_VSYNC_WAIT_TIME)); + if (ret > 0) { + ret = 0; + } else { + pr_err("%s timeout %x\n", __func__, ret); + ret = -ETIMEDOUT; + } + QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0); + } else { + time_end = (u32)ktime_to_ms(ktime_get()) + + QPIC_MAX_VSYNC_WAIT_TIME; + while (1) { + data = QPIC_INP(QPIC_REG_QPIC_LCDC_STTS); + data &= 0x3F; + if (data == 0) + break; + /* yield 10 us for next polling by experiment*/ + usleep_range(10, 10); + if (ktime_to_ms(ktime_get()) > time_end) { + pr_err("%s time out", __func__); + ret = -EBUSY; + break; + } + } + } + return ret; +} + +static int qpic_wait_for_eof(void) +{ + u32 data, time_end; + int ret = 0; + if (use_irq) { + data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS); + if (data & (1 << 2)) + return ret; + reinit_completion(&qpic_res->fifo_eof_comp); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, (1 << 2)); + ret = wait_for_completion_timeout(&qpic_res->fifo_eof_comp, + msecs_to_jiffies(QPIC_MAX_VSYNC_WAIT_TIME)); + if (ret > 0) { + ret = 0; + } else { + pr_err("%s timeout %x\n", __func__, ret); + ret = -ETIMEDOUT; + } + QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0); + } else { + time_end = (u32)ktime_to_ms(ktime_get()) + + QPIC_MAX_VSYNC_WAIT_TIME; + while (1) { + data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS); + if (data & (1 << 2)) + break; + /* yield 10 us for next polling by experiment*/ + usleep_range(10, 10); + if (ktime_to_ms(ktime_get()) > time_end) { + pr_err("%s wait for eof time out\n", __func__); + qpic_dump_reg(); + ret = -EBUSY; + break; + } + } + } + return ret; +} + +static int qpic_send_pkt_sw(u32 cmd, u32 len, u8 *param) +{ + u32 bytes_left, data, cfg2; + int i, ret = 0; + if (len <= 4) { + data = 0; + if (param) { + for (i = 0; i < len; i++) + data |= (u32)param[i] << (8 * i); + } + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT, len); + QPIC_OUTP(QPIC_REG_LCD_DEVICE_CMD0 + (4 * cmd), data); + return 0; + } + + if ((len & 0x1) != 0) { + pr_debug("%s: number of bytes needs be even", __func__); + len = (len + 1) & (~0x1); + } + QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT, 0); + cfg2 = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2); + if ((cmd != OP_WRITE_MEMORY_START) && + (cmd != OP_WRITE_MEMORY_CONTINUE)) + cfg2 |= (1 << 24); /* transparent mode */ + else + cfg2 &= ~(1 << 24); + + cfg2 &= ~0xFF; + cfg2 |= cmd; + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_SOF, 0x0); + bytes_left = len; + + i = 0; + while (bytes_left > 0) { + ret = qpic_wait_for_fifo(); + if (ret) + goto exit_send_cmd_sw; + + data = (u32)param[i]; + + QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_DATA_PORT0, data); + bytes_left--; + i++; + } + /* finished */ + QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_EOF, 0x0); + ret = qpic_wait_for_eof(); +exit_send_cmd_sw: + cfg2 &= ~(1 << 24); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2); + return ret; +} + +int qpic_send_pkt(u32 cmd, u8 *param, u32 len) +{ + if (!use_bam || ((cmd != OP_WRITE_MEMORY_CONTINUE) && + (cmd != OP_WRITE_MEMORY_START))) + return qpic_send_pkt_sw(cmd, len, param); + else + return qpic_send_pkt_bam(cmd, len, param); +} +EXPORT_SYMBOL(qpic_send_pkt); + +int mdss_qpic_init(void) +{ + int ret = 0; + u32 data; + mdss_qpic_reset(); + + pr_info("%s version=%x", __func__, QPIC_INP(QPIC_REG_LCDC_VERSION)); + data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL); + /* clear vsync wait , bam mode = 0*/ + data &= ~(3 << 0); + data &= ~(0x1f << 3); + data |= (1 << 3); /* threshold */ + data |= (1 << 8); /* lcd_en */ + data &= ~(0x1f << 9); + data |= (1 << 9); /* threshold */ + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data); + + if (use_irq && (!qpic_res->irq_requested)) { + ret = devm_request_irq(&qpic_res->pdev->dev, + qpic_res->irq, qpic_irq_handler, + 0, "QPIC", qpic_res); + if (ret) { + pr_err("qpic request_irq() failed!\n"); + use_irq = false; + } else { + disable_irq(qpic_res->irq); + } + qpic_res->irq_requested = true; + } + + qpic_interrupt_en(use_irq); + + data = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2); + data &= ~(0xFFF); + data |= 0x200; /* XRGB */ + data |= 0x2C; + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, data); + + if (use_bam) { + if (!qpic_res->chan) { + qpic_res->chan = dma_request_slave_channel( + &qpic_res->pdev->dev, "chan"); + if (!qpic_res->chan) { + dev_err(&qpic_res->pdev->dev, + "failed to request channel\n"); + return -ENODEV; + } + } + + data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL); + data |= (1 << 1); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data); + } + + /* TE enable */ + if (use_vsync) { + data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL); + data |= (1 << 0); + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data); + } + + return ret; +} + +u32 qpic_read_data(u32 cmd_index, u32 size) +{ + u32 data = 0; + if (size <= 4) { + QPIC_OUTP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT, size); + data = QPIC_INP(QPIC_REG_LCD_DEVICE_CMD0 + (cmd_index * 4)); + } + return data; +} +EXPORT_SYMBOL(qpic_read_data); + +static int mdss_qpic_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + int rc = 0; + u32 bam_timeout; + static struct msm_mdp_interface qpic_interface = { + .init_fnc = mdss_qpic_overlay_init, + .fb_mem_alloc_fnc = mdss_qpic_alloc_fb_mem, + .fb_stride = mdss_qpic_fb_stride, + }; + + + if (!pdev->dev.of_node) { + pr_err("qpic driver only supports device tree probe\n"); + return -ENOTSUPP; + } + + if (!qpic_res) + qpic_res = devm_kzalloc(&pdev->dev, + sizeof(*qpic_res), GFP_KERNEL); + + if (!qpic_res) + return -ENOMEM; + + if (qpic_res->res_init) { + pr_err("qpic already initialized\n"); + return -EINVAL; + } + + qpic_res->core_clk = devm_clk_get(dev, "core"); + if (IS_ERR(qpic_res->core_clk)) + return PTR_ERR(qpic_res->core_clk); + + qpic_res->aon_clk = devm_clk_get(dev, "aon"); + if (IS_ERR(qpic_res->aon_clk)) + return PTR_ERR(qpic_res->aon_clk); + + rc = clk_prepare_enable(qpic_res->core_clk); + if (rc) + return rc; + + rc = clk_prepare_enable(qpic_res->aon_clk); + if (rc) + goto err_aon_clk; + + pdev->id = 0; + + qpic_res->pdev = pdev; + platform_set_drvdata(pdev, qpic_res); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("unable to get QPIC reg base address\n"); + rc = -ENOMEM; + goto clk_disable; + } + + qpic_res->qpic_reg_size = resource_size(res); + qpic_res->qpic_base = devm_ioremap(&pdev->dev, res->start, + qpic_res->qpic_reg_size); + if (unlikely(!qpic_res->qpic_base)) { + pr_err("unable to map MDSS QPIC base\n"); + rc = -ENOMEM; + goto clk_disable; + } + qpic_res->qpic_phys = res->start; + pr_info("MDSS QPIC HW Base phy_Address=0x%x virt=%p\n", + (int) res->start, + (void *)qpic_res->qpic_base); + + qpic_res->irq = platform_get_irq(pdev, 0); + if (qpic_res->irq < 0) { + dev_warn(&pdev->dev, "missing 'lcdc_irq' resource entry"); + rc = -EINVAL; + goto clk_disable; + } + + if (of_property_read_u32(pdev->dev.of_node, + "qti,bam_timeout", &bam_timeout)) + bam_timeout = QPIC_BAM_TIMEOUT; + + qpic_res->bam_timeout = msecs_to_jiffies(bam_timeout); + + qpic_res->res_init = true; + init_completion(&qpic_res->completion); + + rc = mdss_fb_register_mdp_instance(&qpic_interface); + if (rc) { + pr_err("unable to register QPIC instance\n"); + goto clk_disable; + } + + return 0; + +clk_disable: + clk_disable_unprepare(qpic_res->aon_clk); +err_aon_clk: + clk_disable_unprepare(qpic_res->core_clk); + + return rc; +} + +static int mdss_qpic_remove(struct platform_device *pdev) +{ + if (qpic_res->chan) + dma_release_channel(qpic_res->chan); + + if (qpic_res->fb_virt) + mdss_qpic_dma_pages_ops(qpic_res->fb_phys, + qpic_res->fb_size, put_page); + + if (!qpic_res->cmd_buf_virt) + dma_free_wc(&qpic_res->pdev->dev, + QPIC_MAX_CMD_BUF_SIZE, + qpic_res->cmd_buf_virt, + qpic_res->cmd_buf_phys); + + clk_disable_unprepare(qpic_res->aon_clk); + clk_disable_unprepare(qpic_res->core_clk); + + return 0; +} + +static int __init mdss_qpic_driver_init(void) +{ + int ret; + + ret = platform_driver_register(&mdss_qpic_driver); + if (ret) + pr_err("mdss_qpic_register_driver() failed!\n"); + return ret; +} + +module_init(mdss_qpic_driver_init); + +
diff --git a/drivers/video/fbdev/qti/mdss_qpic.h b/drivers/video/fbdev/qti/mdss_qpic.h new file mode 100644 index 0000000..36ca681 --- /dev/null +++ b/drivers/video/fbdev/qti/mdss_qpic.h
@@ -0,0 +1,98 @@ +/* Copyright (c) 2014, 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MDSS_QPIC_H +#define MDSS_QPIC_H + +#include <linux/list.h> + +#include <linux/pinctrl/consumer.h> +#include "mdss_panel.h" +#include "mdss_qpic_panel.h" + +#define QPIC_REG_QPIC_LCDC_CTRL 0x22000 +#define QPIC_REG_LCDC_VERSION 0x22004 +#define QPIC_REG_QPIC_LCDC_IRQ_EN 0x22008 +#define QPIC_REG_QPIC_LCDC_IRQ_STTS 0x2200C +#define QPIC_REG_QPIC_LCDC_IRQ_CLR 0x22010 +#define QPIC_REG_QPIC_LCDC_STTS 0x22014 +#define QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT 0x22018 +#define QPIC_REG_QPIC_LCDC_CFG0 0x22020 +#define QPIC_REG_QPIC_LCDC_CFG1 0x22024 +#define QPIC_REG_QPIC_LCDC_CFG2 0x22028 +#define QPIC_REG_QPIC_LCDC_RESET 0x2202C +#define QPIC_REG_QPIC_LCDC_FIFO_SOF 0x22100 +#define QPIC_REG_LCD_DEVICE_CMD0 0x23000 +#define QPIC_REG_QPIC_LCDC_FIFO_DATA_PORT0 0x22140 +#define QPIC_REG_QPIC_LCDC_FIFO_EOF 0x22180 + +#define QPIC_OUTP(off, data) \ + writel_relaxed((data), qpic_res->qpic_base + (off)) +#define QPIC_OUTPW(off, data) \ + writew_relaxed((data), qpic_res->qpic_base + (off)) +#define QPIC_INP(off) \ + readl_relaxed(qpic_res->qpic_base + (off)) + +#define QPIC_MAX_VSYNC_WAIT_TIME 500 +#define QPIC_MAX_CMD_BUF_SIZE 512 + +#define QPIC_BAM_TIMEOUT 400 + +int mdss_qpic_init(void); +void mdss_qpic_set_cfg0(void); +int qpic_send_pkt(u32 cmd, u8 *param, u32 len); +u32 qpic_read_data(u32 cmd_index, u32 size); +int mdss_qpic_panel_on(struct mdss_panel_data *pdata, + struct qpic_panel_io_desc *panel_io); +int mdss_qpic_panel_off(struct mdss_panel_data *pdata, + struct qpic_panel_io_desc *panel_io); +int qpic_register_panel(struct mdss_panel_data *pdata); + +struct qpic_data_type { + u32 rev; + struct platform_device *pdev; + size_t qpic_reg_size; + u32 qpic_phys; + char __iomem *qpic_base; + int irq; + int bam_irq; + u32 irq_ena; + u32 res_init; + void *fb_virt; + u32 fb_phys; + size_t fb_size; + void *cmd_buf_virt; + u32 cmd_buf_phys; + u32 irq_requested; + struct mdss_panel_data *panel_data; + struct qpic_panel_io_desc panel_io; + u32 bus_handle; + unsigned long bam_timeout; + struct completion fifo_eof_comp; + struct completion completion; + struct dma_chan *chan; + struct clk *core_clk; + struct clk *aon_clk; +}; + +u32 qpic_send_frame( + u32 x_start, + u32 y_start, + u32 x_end, + u32 y_end, + u32 *data, + u32 total_bytes); + +u32 qpic_panel_get_framerate(void); + +#endif /* MDSS_QPIC_H */
diff --git a/drivers/video/fbdev/qti/mdss_qpic_panel.c b/drivers/video/fbdev/qti/mdss_qpic_panel.c new file mode 100644 index 0000000..dd9d9ba --- /dev/null +++ b/drivers/video/fbdev/qti/mdss_qpic_panel.c
@@ -0,0 +1,199 @@ +/* Copyright (c) 2014, 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/of.h> + +#include "mdss_panel.h" +#include "mdss_qpic.h" +#include "mdss_qpic_panel.h" + +static u32 panel_is_on; +static u32 panel_refresh_rate; + +int (*qpic_panel_on)(struct qpic_panel_io_desc *qpic_panel_io); +EXPORT_SYMBOL(qpic_panel_on); +void (*qpic_panel_off)(struct qpic_panel_io_desc *qpic_panel_io); +EXPORT_SYMBOL(qpic_panel_off); + +u32 qpic_panel_get_framerate(void) +{ + return panel_refresh_rate; +} + +/* write a frame of pixels to a MIPI screen */ +u32 qpic_send_frame(u32 x_start, + u32 y_start, + u32 x_end, + u32 y_end, + u32 *data, + u32 total_bytes) +{ + u8 param[4]; + u32 status; + u32 start_0_7; + u32 end_0_7; + u32 start_8_15; + u32 end_8_15; + + /* convert to 16 bit representation */ + x_start = x_start & 0xffff; + y_start = y_start & 0xffff; + x_end = x_end & 0xffff; + y_end = y_end & 0xffff; + + /* set column/page */ + start_0_7 = x_start & 0xff; + end_0_7 = x_end & 0xff; + start_8_15 = (x_start >> 8) & 0xff; + end_8_15 = (x_end >> 8) & 0xff; + param[0] = start_8_15; + param[1] = start_0_7; + param[2] = end_8_15; + param[3] = end_0_7; + status = qpic_send_pkt(OP_SET_COLUMN_ADDRESS, param, 4); + if (status) { + pr_err("Failed to set column address"); + return status; + } + + start_0_7 = y_start & 0xff; + end_0_7 = y_end & 0xff; + start_8_15 = (y_start >> 8) & 0xff; + end_8_15 = (y_end >> 8) & 0xff; + param[0] = start_8_15; + param[1] = start_0_7; + param[2] = end_8_15; + param[3] = end_0_7; + status = qpic_send_pkt(OP_SET_PAGE_ADDRESS, param, 4); + if (status) { + pr_err("Failed to set page address"); + return status; + } + + status = qpic_send_pkt(OP_WRITE_MEMORY_START, (u8 *)data, total_bytes); + if (status) { + pr_err("Failed to start memory write"); + return status; + } + return 0; +} + +int mdss_qpic_panel_on(struct mdss_panel_data *pdata, + struct qpic_panel_io_desc *panel_io) +{ + int rc = 0; + + if (panel_is_on) + return 0; + mdss_qpic_init(); + + if (!panel_io->init) { + panel_io->init = true; + } + + if (qpic_panel_on) + rc = qpic_panel_on(panel_io); + if (rc) + return rc; + panel_is_on = true; + mdss_qpic_set_cfg0(); + return 0; +} + +int mdss_qpic_panel_off(struct mdss_panel_data *pdata, + struct qpic_panel_io_desc *panel_io) +{ + if (qpic_panel_off) + qpic_panel_off(panel_io); + + panel_is_on = false; + return 0; +} + +static int mdss_panel_parse_dt(struct platform_device *pdev, + struct mdss_panel_data *panel_data) +{ + struct device_node *np = pdev->dev.of_node; + u32 res[6], tmp; + int rc; + + rc = of_property_read_u32_array(np, "qti,mdss-pan-res", res, 2); + if (rc) { + pr_err("%s:%d, panel resolution not specified\n", + __func__, __LINE__); + return -EINVAL; + } + panel_data->panel_info.xres = (!rc ? res[0] : 240); + panel_data->panel_info.yres = (!rc ? res[1] : 320); + rc = of_property_read_u32(np, "qti,mdss-pan-bpp", &tmp); + if (rc) { + pr_err("%s:%d, panel bpp not specified\n", + __func__, __LINE__); + return -EINVAL; + } + panel_data->panel_info.bpp = (!rc ? tmp : 24); + of_property_read_u32(np, "qti,refresh_rate", &panel_refresh_rate); + + panel_data->panel_info.type = EBI2_PANEL; + panel_data->panel_info.pdest = DISPLAY_1; + + return rc; +} + +static int mdss_qpic_panel_probe(struct platform_device *pdev) +{ + int rc = 0; + static struct mdss_panel_data vendor_pdata; + static const char *panel_name; + + pr_debug("%s:%d, debug info id=%d", __func__, __LINE__, pdev->id); + if (!pdev->dev.of_node) + return -ENODEV; + + panel_name = of_get_property(pdev->dev.of_node, "label", NULL); + if (!panel_name) + pr_info("%s:%d, panel name not specified\n", + __func__, __LINE__); + else + pr_info("%s: Panel Name = %s\n", __func__, panel_name); + + rc = mdss_panel_parse_dt(pdev, &vendor_pdata); + if (rc) + return rc; + + rc = qpic_register_panel(&vendor_pdata); + if (rc) + return rc; + + return 0; +} + +static const struct of_device_id mdss_qpic_panel_match[] = { + {.compatible = "qti,mdss-qpic-panel"}, + {} +}; + +static struct platform_driver this_driver = { + .probe = mdss_qpic_panel_probe, + .driver = { + .name = "qpic_panel", + .of_match_table = mdss_qpic_panel_match, + }, +}; + +static int __init mdss_qpic_panel_init(void) +{ + return platform_driver_register(&this_driver); +} +MODULE_DEVICE_TABLE(of, mdss_qpic_panel_match); +module_init(mdss_qpic_panel_init);
diff --git a/drivers/video/fbdev/qti/mdss_qpic_panel.h b/drivers/video/fbdev/qti/mdss_qpic_panel.h new file mode 100644 index 0000000..f5a49ec --- /dev/null +++ b/drivers/video/fbdev/qti/mdss_qpic_panel.h
@@ -0,0 +1,137 @@ +/* Copyright (c) 2014, 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MDSS_QPIC_PANEL_H +#define MDSS_QPIC_PANEL_H + +#include <linux/list.h> + +#include "mdss_panel.h" + +#define LCDC_INTERNAL_BUFFER_SIZE 30 + +/** + Macros for coding MIPI commands +*/ +#define INV_SIZE 0xFFFF +/* Size of argument to MIPI command is variable */ +#define OP_SIZE_PAIR(op, size) ((op<<16) | size) +/* MIPI {command, argument size} tuple */ +#define LCDC_EXTRACT_OP_SIZE(op_identifier) ((op_identifier&0xFFFF)) +/* extract size from command identifier */ +#define LCDC_EXTRACT_OP_CMD(op_identifier) (((op_identifier>>16)&0xFFFF)) +/* extract command id from command identifier */ + + +/* MIPI standard efinitions */ +#define LCDC_ADDRESS_MODE_ORDER_BOTTOM_TO_TOP 0x80 +#define LCDC_ADDRESS_MODE_ORDER_RIGHT_TO_LEFT 0x40 +#define LCDC_ADDRESS_MODE_ORDER_REVERSE 0x20 +#define LCDC_ADDRESS_MODE_ORDER_REFRESH_BOTTOM_TO_TOP 0x10 +#define LCDC_ADDRESS_MODE_ORDER_BGER_RGB 0x08 +#define LCDC_ADDRESS_MODE_ORDER_REFERESH_RIGHT_TO_LEFT 0x04 +#define LCDC_ADDRESS_MODE_FLIP_HORIZONTAL 0x02 +#define LCDC_ADDRESS_MODE_FLIP_VERTICAL 0x01 + +#define LCDC_PIXEL_FORMAT_3_BITS_PER_PIXEL 0x1 +#define LCDC_PIXEL_FORMAT_8_BITS_PER_PIXEL 0x2 +#define LCDC_PIXEL_FORMAT_12_BITS_PER_PIXEL 0x3 +#define LCDC_PIXEL_FORMAT_16_BITS_PER_PIXEL 0x5 +#define LCDC_PIXEL_FORMAT_18_BITS_PER_PIXEL 0x6 +#define LCDC_PIXEL_FORMAT_24_BITS_PER_PIXEL 0x7 + +#define LCDC_CREATE_PIXEL_FORMAT(dpi_format, dbi_format) \ + (dpi_format | (dpi_format<<4)) + +#define POWER_MODE_IDLE_ON 0x40 +#define POWER_MODE_PARTIAL_ON 0x20 +#define POWER_MODE_SLEEP_ON 0x10 +#define POWER_MODE_NORMAL_ON 0x08 +#define POWER_MODE_DISPLAY_ON 0x04 + +#define LCDC_DISPLAY_MODE_SCROLLING_ON 0x80 +#define LCDC_DISPLAY_MODE_INVERSION_ON 0x20 +#define LCDC_DISPLAY_MODE_GAMMA_MASK 0x07 + +/** + * LDCc MIPI Type B supported commands + */ +#define OP_ENTER_IDLE_MODE 0x39 +#define OP_ENTER_INVERT_MODE 0x21 +#define OP_ENTER_NORMAL_MODE 0x13 +#define OP_ENTER_PARTIAL_MODE 0x12 +#define OP_ENTER_SLEEP_MODE 0x10 +#define OP_EXIT_INVERT_MODE 0x20 +#define OP_EXIT_SLEEP_MODE 0x11 +#define OP_EXIT_IDLE_MODE 0x38 +#define OP_GET_ADDRESS_MODE 0x0B /* size 1 */ +#define OP_GET_BLUE_CHANNEL 0x08 /* size 1 */ +#define OP_GET_DIAGNOSTIC 0x0F /* size 2 */ +#define OP_GET_DISPLAY_MODE 0x0D /* size 1 */ +#define OP_GET_GREEN_CHANNEL 0x07 /* size 1 */ +#define OP_GET_PIXEL_FORMAT 0x0C /* size 1 */ +#define OP_GET_POWER_MODE 0x0A /* size 1 */ +#define OP_GET_RED_CHANNEL 0x06 /* size 1 */ +#define OP_GET_SCANLINE 0x45 /* size 1 */ +#define OP_GET_SIGNAL_MODE 0x0E /* size 1 */ +#define OP_NOP 0x00 +#define OP_READ_DDB_CONTINUE 0xA8 /* size not fixed */ +#define OP_READ_DDB_START 0xA1 /* size not fixed */ +#define OP_READ_MEMORY_CONTINUE 0x3E /* size not fixed */ +#define OP_READ_MEMORY_START 0x2E /* size not fixed */ +#define OP_SET_ADDRESS_MODE 0x36 /* size 1 */ +#define OP_SET_COLUMN_ADDRESS 0x2A /* size 4 */ +#define OP_SET_DISPLAY_OFF 0x28 +#define OP_SET_DISPLAY_ON 0x29 +#define OP_SET_GAMMA_CURVE 0x26 /* size 1 */ +#define OP_SET_PAGE_ADDRESS 0x2B /* size 4 */ +#define OP_SET_PARTIAL_COLUMNS 0x31 /* size 4 */ +#define OP_SET_PARTIAL_ROWS 0x30 /* size 4 */ +#define OP_SET_PIXEL_FORMAT 0x3A /* size 1 */ +#define OP_SOFT_RESET 0x01 +#define OP_WRITE_MEMORY_CONTINUE 0x3C /* size not fixed */ +#define OP_WRITE_MEMORY_START 0x2C /* size not fixed */ + +/** + * ILI9341 commands + */ +#define OP_ILI9341_INTERFACE_CONTROL 0xf6 +#define OP_ILI9341_TEARING_EFFECT_LINE_ON 0x35 + +struct qpic_pinctrl_res { + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; +}; + +struct qpic_panel_io_desc { + int rst_gpio; + int cs_gpio; + int ad8_gpio; + int te_gpio; + int bl_gpio; + struct regulator *vdd_vreg; + struct regulator *avdd_vreg; + u32 init; + struct qpic_pinctrl_res pin_res; +}; + +int mdss_qpic_panel_io_init(struct platform_device *pdev, + struct qpic_panel_io_desc *qpic_panel_io); +u32 qpic_panel_get_cmd(u32 command, u32 size); +int ili9341_on(struct qpic_panel_io_desc *qpic_panel_io); +void ili9341_off(struct qpic_panel_io_desc *qpic_panel_io); +extern int (*qpic_panel_on)(struct qpic_panel_io_desc *qpic_panel_io); +extern void (*qpic_panel_off)(struct qpic_panel_io_desc *qpic_panel_io); + +#endif /* MDSS_QPIC_PANEL_H */
diff --git a/drivers/video/fbdev/qti/qpic_panel_er_ssd1963.c b/drivers/video/fbdev/qti/qpic_panel_er_ssd1963.c new file mode 100644 index 0000000..b760f7c --- /dev/null +++ b/drivers/video/fbdev/qti/qpic_panel_er_ssd1963.c
@@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/delay.h> + +#include "mdss_qpic.h" +#include "mdss_qpic_panel.h" +#include "qpic_panel_er_ssd1963.h" + +void ertft_off(struct qpic_panel_io_desc *qpic_panel_io) +{ + /* TODO : add shutdown related code if needed */ +} + +int ertft_on(struct qpic_panel_io_desc *qpic_panel_io) +{ + u8 param[8]; + + /* Set PLL MN */ + param[0] = 0x23; + param[1] = 0x02; + param[2] = 0x54; + qpic_send_pkt(OP_SSD1963_SET_PLL_MN, param, 3); + + /* Use PLL output as system clock */ + param[0] = 0x1; + qpic_send_pkt(OP_SSD1963_SET_PLL, param, 1); + msleep(120); + + /* Enable PLL */ + param[0] = 0x03; + qpic_send_pkt(OP_SSD1963_SET_PLL, param, 1); + msleep(120); + + /* Software Reset */ + qpic_send_pkt(OP_SOFT_RESET, NULL, 0); + /* wait for 120 ms after reset as panel spec suggests */ + msleep(120); + + /* Set the LSHIFT (pixel clock) frequency */ + param[0] = 0x04; + param[1] = 0x66; + param[2] = 0x65; + qpic_send_pkt(OP_SSD1963_SET_LSHIFT_FREQ, param, 3); + msleep(20); + + /* Set the LCD panel mode and resolution */ + param[0] = 0x20; + param[1] = 0x00; + param[2] = 0x03; + param[3] = 0x1f; + param[4] = 0x01; + param[5] = 0xdf; + param[6] = 0x00; + qpic_send_pkt(OP_SSD1963_SET_LCD_MODE, param, 7); + msleep(20); + + /* Set front porch */ + param[0] = 0x04; + param[1] = 0x1f; + param[2] = 0x00; + param[3] = 0xd2; + param[4] = 0x00; + param[5] = 0x00; + param[6] = 0x00; + param[7] = 0x00; + qpic_send_pkt(OP_SSD1963_SET_HOR_PERIOD, param, 8); + + /* Set the vertical blanking interval between last scan line and + * next LFRAME pulse + */ + param[0] = 0x02; + param[1] = 0x0c; + param[2] = 0x00; + param[3] = 0x22; + param[4] = 0x00; + param[5] = 0x00; + param[6] = 0x00; + qpic_send_pkt(OP_SSD1963_SET_VER_PERIOD, param, 7); + msleep(20); + + /* Set GPIO config */ + param[0] = 0x0f; + param[1] = 0x01; + qpic_send_pkt(OP_SSD1963_SET_GPIO_CONFIG, param, 2); + msleep(20); + + /* Set GPIO Value */ + param[0] = 0x1; + qpic_send_pkt(OP_SSD1963_SET_GPIO_VAL, param, 1); + + /* set memory access control */ + param[0] = 0x08; + qpic_send_pkt(OP_SET_ADDRESS_MODE, param, 1); + /* wait for 20 ms after command sent as panel spec suggests */ + msleep(20); + + param[0] = 0x60; + qpic_send_pkt(OP_SET_PIXEL_FORMAT, param, 1); + + /* Set the pixel data format of the parallel host processor + * interface + */ + param[0] = 0x06; + qpic_send_pkt(OP_SSD1963_SET_PIXEL_DATA_IFACE, param, 1); + /* wait for 20 ms after command sent as panel spec suggests */ + msleep(20); + + /* set post proc */ + param[0] = 0x40; + param[1] = 0x80; + param[2] = 0x40; + param[3] = 0x01; + qpic_send_pkt(OP_SSD1963_SET_POST_PROC, param, 4); + /* wait for 20 ms after command sent */ + msleep(20); + + /* display on */ + qpic_send_pkt(OP_SET_DISPLAY_ON, NULL, 0); + /* wait for 20 ms after command sent as panel spec suggests */ + msleep(20); + + param[0] = 0x06; + param[1] = 0x80; + param[2] = 0x01; + param[3] = 0xf0; + param[4] = 0x00; + param[5] = 0x00; + qpic_send_pkt(OP_SSD1963_SET_PWM_CONFIG, param, 6); + /* wait for 20 ms after command sent as panel spec suggests */ + msleep(20); + + param[0] = 0x0d; + qpic_send_pkt(OP_SSD1963_SET_DBC_CONFIG, param, 1); + /* wait for 20 ms after command sent as panel spec suggests */ + msleep(20); + + param[0] = 0x00; + qpic_send_pkt(OP_SSD1963_TEARING_EFFECT_LINE_ON, param, 1); + + param[0] = qpic_read_data(OP_GET_PIXEL_FORMAT, 1); + pr_info("Pixel format =%x", param[0]); + + return 0; +} + +static int __init mdss_qpic_ertft_panel_init(void) +{ + qpic_panel_on = ertft_on; + qpic_panel_off = ertft_off; + return 0; +} + +static void __exit mdss_qpic_ertft_panel_exit(void) +{ + qpic_panel_on = NULL; + qpic_panel_off = NULL; +} +module_init(mdss_qpic_ertft_panel_init); +module_exit(mdss_qpic_ertft_panel_exit); + +MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/video/fbdev/qti/qpic_panel_er_ssd1963.h b/drivers/video/fbdev/qti/qpic_panel_er_ssd1963.h new file mode 100644 index 0000000..e741737 --- /dev/null +++ b/drivers/video/fbdev/qti/qpic_panel_er_ssd1963.h
@@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MDSS_QPIC_ERTFT_PANEL_H +#define MDSS_QPIC_ERTFT_PANEL_H + +/** + * SSD1963 commands. ER panel */ +#define OP_SSD1963_TEARING_EFFECT_LINE_ON 0x35 +#define OP_SSD1963_SET_LCD_MODE 0xB0 +#define OP_SSD1963_SET_HOR_PERIOD 0xB4 +#define OP_SSD1963_SET_VER_PERIOD 0xB6 +#define OP_SSD1963_SET_GPIO_CONFIG 0xB8 +#define OP_SSD1963_SET_GPIO_VAL 0xBA +#define OP_SSD1963_SET_POST_PROC 0xBC +#define OP_SSD1963_SET_PWM_CONFIG 0xBE +#define OP_SSD1963_SET_PLL 0xE0 +#define OP_SSD1963_SET_PLL_MN 0xE2 +#define OP_SSD1963_SET_LSHIFT_FREQ 0xE6 +#define OP_SSD1963_SET_PIXEL_DATA_IFACE 0xF0 +#define OP_SSD1963_SET_DBC_CONFIG 0xD0 + +#endif
diff --git a/drivers/video/fbdev/riva/fbdev.c b/drivers/video/fbdev/riva/fbdev.c index 51c9d95..ca593a3 100644 --- a/drivers/video/fbdev/riva/fbdev.c +++ b/drivers/video/fbdev/riva/fbdev.c
@@ -1088,9 +1088,6 @@ static int rivafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) int mode_valid = 0; NVTRACE_ENTER(); - if (!var->pixclock) - return -EINVAL; - switch (var->bits_per_pixel) { case 1 ... 8: var->red.offset = var->green.offset = var->blue.offset = 0;
diff --git a/drivers/video/fbdev/sm712fb.c b/drivers/video/fbdev/sm712fb.c index 83d0560..2466814 100644 --- a/drivers/video/fbdev/sm712fb.c +++ b/drivers/video/fbdev/sm712fb.c
@@ -1047,7 +1047,7 @@ static ssize_t smtcfb_read(struct fb_info *info, char __user *buf, if (count + p > total_size) count = total_size - p; - buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL); if (!buffer) return -ENOMEM; @@ -1059,14 +1059,25 @@ static ssize_t smtcfb_read(struct fb_info *info, char __user *buf, while (count) { c = (count > PAGE_SIZE) ? PAGE_SIZE : count; dst = buffer; - for (i = (c + 3) >> 2; i--;) { - u32 val; - - val = fb_readl(src); - *dst = big_swap(val); - src++; + for (i = c >> 2; i--;) { + *dst = fb_readl(src++); + *dst = big_swap(*dst); dst++; } + if (c & 3) { + u8 *dst8 = (u8 *)dst; + u8 __iomem *src8 = (u8 __iomem *)src; + + for (i = c & 3; i--;) { + if (i & 1) { + *dst8++ = fb_readb(++src8); + } else { + *dst8++ = fb_readb(--src8); + src8 += 2; + } + } + src = (u32 __iomem *)src8; + } if (copy_to_user(buf, buffer, c)) { err = -EFAULT; @@ -1119,7 +1130,7 @@ static ssize_t smtcfb_write(struct fb_info *info, const char __user *buf, count = total_size - p; } - buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL); if (!buffer) return -ENOMEM; @@ -1137,11 +1148,24 @@ static ssize_t smtcfb_write(struct fb_info *info, const char __user *buf, break; } - for (i = (c + 3) >> 2; i--;) { - fb_writel(big_swap(*src), dst); - dst++; + for (i = c >> 2; i--;) { + fb_writel(big_swap(*src), dst++); src++; } + if (c & 3) { + u8 *src8 = (u8 *)src; + u8 __iomem *dst8 = (u8 __iomem *)dst; + + for (i = c & 3; i--;) { + if (i & 1) { + fb_writeb(*src8++, ++dst8); + } else { + fb_writeb(*src8++, --dst8); + dst8 += 2; + } + } + dst = (u32 __iomem *)dst8; + } *ppos += c; buf += c;
diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c index ad60f73..0e0f5bb 100644 --- a/drivers/video/fbdev/smscufx.c +++ b/drivers/video/fbdev/smscufx.c
@@ -1657,7 +1657,6 @@ static int ufx_usb_probe(struct usb_interface *interface, info->par = dev; info->pseudo_palette = dev->pseudo_palette; info->fbops = &ufx_ops; - INIT_LIST_HEAD(&info->modelist); retval = fb_alloc_cmap(&info->cmap, 256, 0); if (retval < 0) { @@ -1668,6 +1667,8 @@ static int ufx_usb_probe(struct usb_interface *interface, INIT_DELAYED_WORK(&dev->free_framebuffer_work, ufx_free_framebuffer_work); + INIT_LIST_HEAD(&info->modelist); + retval = ufx_reg_read(dev, 0x3000, &id_rev); check_warn_goto_error(retval, "error %d reading 0x3000 register from device", retval); dev_dbg(dev->gdev, "ID_REV register value 0x%08x", id_rev);
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index 8e8af40..fe373b6 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c
@@ -1017,7 +1017,6 @@ static void dlfb_ops_destroy(struct fb_info *info) } vfree(dlfb->backing_buffer); kfree(dlfb->edid); - dlfb_free_urb_list(dlfb); usb_put_dev(dlfb->udev); kfree(dlfb); @@ -1427,7 +1426,7 @@ static ssize_t metrics_bytes_rendered_show(struct device *fbdev, struct device_attribute *a, char *buf) { struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dlfb = fb_info->par; - return sysfs_emit(buf, "%u\n", + return snprintf(buf, PAGE_SIZE, "%u\n", atomic_read(&dlfb->bytes_rendered)); } @@ -1435,7 +1434,7 @@ static ssize_t metrics_bytes_identical_show(struct device *fbdev, struct device_attribute *a, char *buf) { struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dlfb = fb_info->par; - return sysfs_emit(buf, "%u\n", + return snprintf(buf, PAGE_SIZE, "%u\n", atomic_read(&dlfb->bytes_identical)); } @@ -1443,7 +1442,7 @@ static ssize_t metrics_bytes_sent_show(struct device *fbdev, struct device_attribute *a, char *buf) { struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dlfb = fb_info->par; - return sysfs_emit(buf, "%u\n", + return snprintf(buf, PAGE_SIZE, "%u\n", atomic_read(&dlfb->bytes_sent)); } @@ -1451,7 +1450,7 @@ static ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev, struct device_attribute *a, char *buf) { struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dlfb = fb_info->par; - return sysfs_emit(buf, "%u\n", + return snprintf(buf, PAGE_SIZE, "%u\n", atomic_read(&dlfb->cpu_kcycles_used)); } @@ -1650,9 +1649,8 @@ static int dlfb_usb_probe(struct usb_interface *intf, const struct device_attribute *attr; struct dlfb_data *dlfb; struct fb_info *info; - int retval; + int retval = -ENOMEM; struct usb_device *usbdev = interface_to_usbdev(intf); - struct usb_endpoint_descriptor *out; /* usb initialization */ dlfb = kzalloc(sizeof(*dlfb), GFP_KERNEL); @@ -1666,12 +1664,6 @@ static int dlfb_usb_probe(struct usb_interface *intf, dlfb->udev = usb_get_dev(usbdev); usb_set_intfdata(intf, dlfb); - retval = usb_find_common_endpoints(intf->cur_altsetting, NULL, &out, NULL, NULL); - if (retval) { - dev_err(&intf->dev, "Device should have at lease 1 bulk endpoint!\n"); - goto error; - } - dev_dbg(&intf->dev, "console enable=%d\n", console); dev_dbg(&intf->dev, "fb_defio enable=%d\n", fb_defio); dev_dbg(&intf->dev, "shadow enable=%d\n", shadow); @@ -1681,7 +1673,6 @@ static int dlfb_usb_probe(struct usb_interface *intf, if (!dlfb_parse_vendor_descriptor(dlfb, intf)) { dev_err(&intf->dev, "firmware not recognized, incompatible device?\n"); - retval = -ENODEV; goto error; } @@ -1695,10 +1686,8 @@ static int dlfb_usb_probe(struct usb_interface *intf, /* allocates framebuffer driver structure, not framebuffer memory */ info = framebuffer_alloc(0, &dlfb->udev->dev); - if (!info) { - retval = -ENOMEM; + if (!info) goto error; - } dlfb->info = info; info->par = dlfb;
diff --git a/drivers/video/fbdev/w100fb.c b/drivers/video/fbdev/w100fb.c index 52ec80b..e30f942 100644 --- a/drivers/video/fbdev/w100fb.c +++ b/drivers/video/fbdev/w100fb.c
@@ -770,18 +770,12 @@ int w100fb_probe(struct platform_device *pdev) fb_dealloc_cmap(&info->cmap); kfree(info->pseudo_palette); } - if (remapped_fbuf != NULL) { + if (remapped_fbuf != NULL) iounmap(remapped_fbuf); - remapped_fbuf = NULL; - } - if (remapped_regs != NULL) { + if (remapped_regs != NULL) iounmap(remapped_regs); - remapped_regs = NULL; - } - if (remapped_base != NULL) { + if (remapped_base != NULL) iounmap(remapped_base); - remapped_base = NULL; - } if (info) framebuffer_release(info); return err; @@ -801,11 +795,8 @@ static int w100fb_remove(struct platform_device *pdev) fb_dealloc_cmap(&info->cmap); iounmap(remapped_base); - remapped_base = NULL; iounmap(remapped_regs); - remapped_regs = NULL; iounmap(remapped_fbuf); - remapped_fbuf = NULL; framebuffer_release(info);