diff options
Diffstat (limited to 'drivers/video')
59 files changed, 3472 insertions, 1145 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a26bace176..9e176d3198 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menuconfig VIDEO bool "Video drivers" help @@ -14,7 +15,7 @@ config FRAMEBUFFER_CONSOLE config DRIVER_VIDEO_FB_SSD1307 bool "Solomon SSD1307 framebuffer support" - depends on I2C && GPIOLIB + depends on (I2C || SPI) && GPIOLIB config VIDEO_VPL depends on OFTREE @@ -30,7 +31,8 @@ config DRIVER_VIDEO_ATMEL_HLCD config DRIVER_VIDEO_EFI_GOP bool "EFI Graphics Output Protocol (GOP)" - depends on EFI_BOOTUP + depends on EFI_PAYLOAD + depends on X86 config DRIVER_VIDEO_IMX bool "i.MX framebuffer driver" @@ -58,30 +60,26 @@ config DRIVER_VIDEO_STM Say 'Y' here to enable framebuffer and splash screen support for i.MX23 and i.MX28 based systems. -config DRIVER_VIDEO_S3C24XX - bool "S3C244x framebuffer driver" - depends on ARCH_S3C24xx +config DRIVER_VIDEO_STM32_LTDC + bool "STM32 LTDC framebuffer driver" + select VIDEO_VPL + depends on ARCH_STM32 || COMPILE_TEST help - Add support for the S3C244x LCD controller. + Say 'Y' here to enable framebuffer and splash screen support for + STM32 and STM32MP1. config DRIVER_VIDEO_OMAP bool "OMAP framebuffer driver" - depends on ARCH_OMAP4 + depends on ARCH_OMAP4 || COMPILE_TEST help Add support for OMAP Display Controller. Currently this driver only supports OMAP4 SoCs in DISPC parallel mode on LCD2 (MIPI DPI). -if DRIVER_VIDEO_S3C24XX - -config DRIVER_VIDEO_S3C_VERBOSE - bool "S3C244x verbose framebuffer info" - -endif - config DRIVER_VIDEO_SDL bool "SDL framebuffer driver" depends on SANDBOX + select SDL config DRIVER_VIDEO_PXA bool "PXA27x framebuffer driver" @@ -98,20 +96,36 @@ config DRIVER_VIDEO_BCM283X source "drivers/video/imx-ipu-v3/Kconfig" +source "drivers/video/bochs/Kconfig" + +config DRIVER_VIDEO_SIMPLEFB_CLIENT + bool "Simple framebuffer client support" + depends on OFTREE + help + Add support for reusing a previously set up simple framebuffer. + config DRIVER_VIDEO_SIMPLEFB - bool "Simple framebuffer support" + bool "Simple framebuffer fixup support" depends on OFTREE help Add support for setting up the kernel's simple framebuffer driver based on the active barebox framebuffer. +config DRIVER_VIDEO_RAMFB + bool "QEMU RamFB support" + select QEMU_FW_CFG + help + Add support for setting up a QEMU RamFB driver. + config DRIVER_VIDEO_EDID - depends on I2C bool "Add EDID support" help This enabled support for reading and parsing EDID data from an attached monitor. +config DRIVER_VIDEO_MIPI_DBI + bool + config DRIVER_VIDEO_BACKLIGHT bool "Add backlight support" help @@ -161,4 +175,27 @@ config DRIVER_VIDEO_SIMPLE_PANEL Linux Kernel implementation this one is able to understand display-timings nodes so that it's not necessary to keep a list of all known displays with their corresponding timings in barebox. + +config DRIVER_VIDEO_PANEL_ILITEK_ILI9341 + tristate "Ilitek ILI9341 240x320 QVGA panels" + depends on OFTREE && SPI + select DRIVER_VIDEO_MIPI_DBI + select VIDEO_VPL + help + Say Y here if you want to enable support for Ilitek IL9341 + QVGA (240x320) RGB panels. support serial & parallel rgb + interface. + +config DRIVER_VIDEO_PANEL_MIPI_DBI + tristate "DRM support for MIPI DBI compatible panels" + depends on OFTREE && SPI + select DRIVER_VIDEO_MIPI_DBI + select FIRMWARE + select VIDEO_VPL + help + Say Y here if you want to enable support for MIPI DBI compatible + panels. The controller command setup can be provided using a + firmware file. For more information see + https://github.com/notro/panel-mipi-dbi/wiki. + endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 01fabe8809..85cffb5a33 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_VIDEO) += fb.o obj-$(CONFIG_DRIVER_VIDEO_EDID) += edid.o obj-$(CONFIG_OFDEVICE) += of_display_timing.o @@ -8,20 +9,25 @@ obj-$(CONFIG_VIDEO_VPL) += vpl.o obj-$(CONFIG_DRIVER_VIDEO_MTL017) += mtl017.o obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o +obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o +obj-$(CONFIG_DRIVER_VIDEO_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o +obj-$(CONFIG_DRIVER_VIDEO_PANEL_MIPI_DBI) += panel-mipi-dbi.o obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o obj-$(CONFIG_DRIVER_VIDEO_STM) += stm.o +obj-$(CONFIG_DRIVER_VIDEO_STM32_LTDC) += stm32_ltdc.o obj-$(CONFIG_DRIVER_VIDEO_IMX) += imx.o obj-$(CONFIG_DRIVER_VIDEO_IMX_IPU) += imx-ipu-fb.o -obj-$(CONFIG_DRIVER_VIDEO_S3C24XX) += s3c24xx.o obj-$(CONFIG_DRIVER_VIDEO_PXA) += pxa.o obj-$(CONFIG_DRIVER_VIDEO_SDL) += sdl.o obj-$(CONFIG_DRIVER_VIDEO_OMAP) += omap.o obj-$(CONFIG_DRIVER_VIDEO_BCM283X) += bcm2835.o -obj-$(CONFIG_DRIVER_VIDEO_SIMPLEFB) += simplefb.o +obj-$(CONFIG_DRIVER_VIDEO_SIMPLEFB_CLIENT) += simplefb-client.o +obj-$(CONFIG_DRIVER_VIDEO_SIMPLEFB) += simplefb-fixup.o +obj-$(CONFIG_DRIVER_VIDEO_RAMFB) += ramfb.o obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += imx-ipu-v3/ obj-$(CONFIG_DRIVER_VIDEO_EFI_GOP) += efi_gop.o obj-$(CONFIG_DRIVER_VIDEO_FB_SSD1307) += ssd1307fb.o obj-$(CONFIG_BACKLIGHT_RAVE_SP) += rave-sp-backlight.o - +obj-$(CONFIG_DRIVER_VIDEO_BOCHS) += bochs/ diff --git a/drivers/video/atmel_hlcdfb.c b/drivers/video/atmel_hlcdfb.c index aa84334b09..0a24493907 100644 --- a/drivers/video/atmel_hlcdfb.c +++ b/drivers/video/atmel_hlcdfb.c @@ -1,30 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for AT91/AT32 LCD Controller * * Copyright (C) 2007 Atmel Corporation - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <common.h> #include <io.h> #include <init.h> #include <linux/clk.h> -#include <mach/hardware.h> -#include <mach/atmel_hlcdc.h> -#include <mach/cpu.h> +#include <mach/at91/hardware.h> +#include <mach/at91/atmel_hlcdc.h> +#include <mach/at91/cpu.h> #include <errno.h> #include "atmel_lcdfb.h" @@ -273,12 +260,12 @@ struct atmel_lcdfb_devdata atmel_hlcdfb_data = { .dma_desc_size = sizeof(struct atmel_hlcd_dma_desc), }; -static int atmel_hlcdc_probe(struct device_d *dev) +static int atmel_hlcdc_probe(struct device *dev) { return atmel_lcdc_register(dev, &atmel_hlcdfb_data); } -static struct driver_d atmel_hlcdc_driver = { +static struct driver atmel_hlcdc_driver = { .name = "atmel_hlcdfb", .probe = atmel_hlcdc_probe, }; diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 322404f322..5d8dc8f8b9 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -1,27 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for AT91/AT32 LCD Controller * * Copyright (C) 2007 Atmel Corporation - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <common.h> #include <io.h> #include <init.h> -#include <mach/hardware.h> +#include <mach/at91/hardware.h> #include <errno.h> #include <linux/clk.h> @@ -236,7 +223,7 @@ struct atmel_lcdfb_devdata atmel_lcdfb_data = { .limit_screeninfo = atmel_lcdfb_limit_screeninfo, }; -static int atmel_lcdc_probe(struct device_d *dev) +static int atmel_lcdc_probe(struct device *dev) { return atmel_lcdc_register(dev, &atmel_lcdfb_data); } @@ -276,8 +263,9 @@ static __maybe_unused struct of_device_id atmel_lcdfb_compatible[] = { { .compatible = "atmel,at32ap-lcdc", .data = &at32ap_config, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, atmel_lcdfb_compatible); -static struct driver_d atmel_lcdc_driver = { +static struct driver atmel_lcdc_driver = { .name = "atmel_lcdfb", .probe = atmel_lcdc_probe, .of_compatible = DRV_OF_COMPAT(atmel_lcdfb_compatible), diff --git a/drivers/video/atmel_lcdfb.h b/drivers/video/atmel_lcdfb.h index 76c0e739e8..7aa058e198 100644 --- a/drivers/video/atmel_lcdfb.h +++ b/drivers/video/atmel_lcdfb.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <fb.h> #include <video/atmel_lcdc.h> @@ -23,7 +24,7 @@ struct atmel_lcdfb_devdata { struct atmel_lcdfb_info { struct fb_info info; void __iomem *mmio; - struct device_d *device; + struct device *device; unsigned int guard_time; unsigned int smem_len; @@ -48,4 +49,4 @@ struct atmel_lcdfb_info { #define ATMEL_LCDC_STOP_NOWAIT (1 << 0) -int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data); +int atmel_lcdc_register(struct device *dev, struct atmel_lcdfb_devdata *data); diff --git a/drivers/video/atmel_lcdfb_core.c b/drivers/video/atmel_lcdfb_core.c index c6ece5b785..9d3e6682b6 100644 --- a/drivers/video/atmel_lcdfb_core.c +++ b/drivers/video/atmel_lcdfb_core.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for AT91/AT32 LCD Controller * * Copyright (C) 2007 Atmel Corporation - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <common.h> @@ -27,7 +14,7 @@ #include <linux/clk.h> #include <malloc.h> -#include <mach/cpu.h> +#include <mach/at91/cpu.h> #include "atmel_lcdfb.h" @@ -75,7 +62,7 @@ static void atmel_lcdc_disable_controller(struct fb_info *fb_info) static int atmel_lcdfb_check_var(struct fb_info *info) { - struct device_d *dev = &info->dev; + struct device *dev = &info->dev; struct atmel_lcdfb_info *sinfo = info->priv; struct fb_videomode *mode = info->mode; unsigned long clk_value_khz; @@ -132,7 +119,7 @@ static int atmel_lcdfb_check_var(struct fb_info *info) = info->bits_per_pixel; break; case 16: - /* Older SOCs use BGR:555 rather than BGR:565. */ + /* Older SOCs use BRG:555 rather than BRG:565. */ if (sinfo->have_intensity_bit) info->green.length = 5; else @@ -142,7 +129,7 @@ static int atmel_lcdfb_check_var(struct fb_info *info) info->red.offset = info->green.length + 5; info->blue.offset = 0; } else { - /* BGR:5X5 mode */ + /* BRG:5X5 mode */ info->red.offset = 0; info->blue.offset = info->green.length + 5; } @@ -159,7 +146,7 @@ static int atmel_lcdfb_check_var(struct fb_info *info) info->red.offset = 16; info->blue.offset = 0; } else { - /* BGR:888 mode */ + /* BRG:888 mode */ info->red.offset = 0; info->blue.offset = 16; } @@ -250,7 +237,7 @@ static struct fb_ops atmel_lcdc_ops = { .fb_disable = atmel_lcdc_disable_controller, }; -static int power_control_init(struct device_d *dev, +static int power_control_init(struct device *dev, struct atmel_lcdfb_info *sinfo, int gpio, bool active_low) @@ -282,7 +269,7 @@ static int power_control_init(struct device_d *dev, } /* - * Syntax: atmel,lcd-wiring-mode: lcd wiring mode "RGB", "BGR" + * Syntax: atmel,lcd-wiring-mode: lcd wiring mode "RGB", "BRG" */ static int of_get_wiring_mode(struct device_node *np, struct atmel_lcdfb_info *sinfo) @@ -293,12 +280,12 @@ static int of_get_wiring_mode(struct device_node *np, ret = of_property_read_string(np, "atmel,lcd-wiring-mode", &mode); if (ret < 0) { /* Not present, use defaults */ - sinfo->lcd_wiring_mode = ATMEL_LCDC_WIRING_BGR; + sinfo->lcd_wiring_mode = ATMEL_LCDC_WIRING_BRG; return 0; } - if (!strcasecmp(mode, "BGR")) { - sinfo->lcd_wiring_mode = ATMEL_LCDC_WIRING_BGR; + if (!strcasecmp(mode, "BRG")) { + sinfo->lcd_wiring_mode = ATMEL_LCDC_WIRING_BRG; } else if (!strcasecmp(mode, "RGB")) { sinfo->lcd_wiring_mode = ATMEL_LCDC_WIRING_RGB; } else { @@ -307,7 +294,7 @@ static int of_get_wiring_mode(struct device_node *np, return 0; } -static int of_get_power_control(struct device_d *dev, +static int of_get_power_control(struct device *dev, struct device_node *np, struct atmel_lcdfb_info *sinfo) { @@ -324,7 +311,7 @@ static int of_get_power_control(struct device_d *dev, return power_control_init(dev, sinfo, gpio, active_low); } -static int lcdfb_of_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo) +static int lcdfb_of_init(struct device *dev, struct atmel_lcdfb_info *sinfo) { struct fb_info *info = &sinfo->info; struct display_timings *modes; @@ -341,7 +328,7 @@ static int lcdfb_of_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo) } /* Required properties */ - display = of_parse_phandle(dev->device_node, "display", 0); + display = of_parse_phandle(dev->of_node, "display", 0); if (!display) { dev_err(dev, "no display phandle\n"); return -ENOENT; @@ -391,7 +378,8 @@ err: return ret; } -static int lcdfb_pdata_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo) +static int lcdfb_pdata_init(struct device *dev, + struct atmel_lcdfb_info *sinfo) { struct atmel_lcdfb_platform_data *pdata; struct fb_info *info; @@ -435,7 +423,7 @@ err: return ret; } -int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data) +int atmel_lcdc_register(struct device *dev, struct atmel_lcdfb_devdata *data) { struct atmel_lcdfb_info *sinfo; const char *bus_clk_name; @@ -463,7 +451,7 @@ int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data) } bus_clk_name = "hck1"; } else { - if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node) + if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->of_node) return -EINVAL; ret = lcdfb_of_init(dev, sinfo); @@ -492,6 +480,7 @@ int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data) sinfo->dma_desc = dma_alloc_coherent(data->dma_desc_size, DMA_ADDRESS_BROKEN); + info->dev.parent = dev; ret = register_framebuffer(info); if (ret != 0) { dev_err(dev, "Failed to register framebuffer\n"); diff --git a/drivers/video/backlight-pwm.c b/drivers/video/backlight-pwm.c index 9111a42d75..87358ca778 100644 --- a/drivers/video/backlight-pwm.c +++ b/drivers/video/backlight-pwm.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * pwm backlight support for barebox * * (C) Copyright 2014 Sascha Hauer, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <common.h> #include <malloc.h> @@ -27,7 +14,7 @@ #include <regulator.h> #include <gpio.h> #include <of_gpio.h> -#include <asm-generic/div64.h> +#include <linux/math64.h> struct pwm_backlight { struct backlight_device backlight; @@ -37,7 +24,6 @@ struct pwm_backlight { unsigned int *levels; int enable_gpio; int enable_active_high; - int max_value; int enabled; unsigned int scale; }; @@ -115,10 +101,10 @@ static int backlight_pwm_set(struct backlight_device *backlight, return backlight_pwm_disable(pwm_backlight); } -static int pwm_backlight_parse_dt(struct device_d *dev, - struct pwm_backlight *pwm_backlight) +static int pwm_backlight_parse_dt(struct device *dev, + struct pwm_backlight *pwm_backlight) { - struct device_node *node = dev->device_node; + struct device_node *node = dev->of_node; struct property *prop; int length; u32 value; @@ -178,13 +164,13 @@ static int pwm_backlight_parse_dt(struct device_d *dev, return 0; } -static int backlight_pwm_of_probe(struct device_d *dev) +static int backlight_pwm_of_probe(struct device *dev) { int ret; struct pwm_backlight *pwm_backlight; struct pwm_device *pwm; - pwm = of_pwm_request(dev->device_node, NULL); + pwm = of_pwm_request(dev->of_node, NULL); if (IS_ERR(pwm)) { dev_err(dev, "Cannot find PWM device\n"); return PTR_ERR(pwm); @@ -206,7 +192,8 @@ static int backlight_pwm_of_probe(struct device_d *dev) pwm_backlight->backlight.slew_time_ms = 100; pwm_backlight->backlight.brightness_set = backlight_pwm_set; - pwm_backlight->backlight.node = dev->device_node; + pwm_backlight->backlight.dev.parent = dev; + pwm_backlight->backlight.node = dev->of_node; ret = backlight_register(&pwm_backlight->backlight); if (ret) @@ -222,8 +209,9 @@ static struct of_device_id backlight_pwm_of_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, backlight_pwm_of_ids); -static struct driver_d backlight_pwm_of_driver = { +static struct driver backlight_pwm_of_driver = { .name = "pwm-backlight", .probe = backlight_pwm_of_probe, .of_compatible = DRV_OF_COMPAT(backlight_pwm_of_ids), diff --git a/drivers/video/backlight.c b/drivers/video/backlight.c index 3913d1c4c9..6c00cc115e 100644 --- a/drivers/video/backlight.c +++ b/drivers/video/backlight.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <driver.h> #include <linux/list.h> diff --git a/drivers/video/bcm2835.c b/drivers/video/bcm2835.c index 3d52f8b6b8..070d1b4902 100644 --- a/drivers/video/bcm2835.c +++ b/drivers/video/bcm2835.c @@ -14,7 +14,8 @@ #include <malloc.h> #include <xfuncs.h> -#include <mach/mbox.h> +#include <of_address.h> +#include <mach/bcm283x/mbox.h> struct bcm2835fb_info { struct fb_info fbi; @@ -53,14 +54,29 @@ static struct fb_ops bcm2835fb_ops = { .fb_disable = bcm2835fb_disable, }; -static int bcm2835fb_probe(struct device_d *dev) +static int bcm2835fb_probe(struct device *dev) { BCM2835_MBOX_STACK_ALIGN(struct msg_fb_query, msg_query); BCM2835_MBOX_STACK_ALIGN(struct msg_fb_setup, msg_setup); struct bcm2835fb_info *info; + struct device_node *soc; u32 w, h; + u64 dma_addr, cpu_addr, _region_size; + phys_addr_t buffer_addr; int ret; + soc = of_find_node_by_path("/soc"); + if (!soc) { + dev_err(dev, "could not find required OF node /soc\n"); + return -ENODEV; + } + + ret = of_dma_get_range(soc, &dma_addr, &cpu_addr, &_region_size); + if (ret) { + dev_err(dev, "OF node /soc has no dma-ranges\n"); + return ret; + } + BCM2835_MBOX_INIT_HDR(msg_query); BCM2835_MBOX_INIT_TAG_NO_REQ(&msg_query->physical_w_h, GET_PHYSICAL_W_H); @@ -99,10 +115,11 @@ static int bcm2835fb_probe(struct device_d *dev) return ret; } + buffer_addr = (msg_setup->allocate_buffer.body.resp.fb_address & ~dma_addr) + cpu_addr; + info = xzalloc(sizeof *info); info->fbi.fbops = &bcm2835fb_ops; - info->fbi.screen_base = - (void *)msg_setup->allocate_buffer.body.resp.fb_address; + info->fbi.screen_base = phys_to_virt(buffer_addr); info->fbi.xres = msg_setup->physical_w_h.body.resp.width; info->fbi.yres = msg_setup->physical_w_h.body.resp.height; info->fbi.bits_per_pixel = 16; @@ -118,6 +135,7 @@ static int bcm2835fb_probe(struct device_d *dev) info->fbi.mode->xres = info->fbi.xres; info->fbi.mode->yres = info->fbi.yres; + info->fbi.dev.parent = dev; ret = register_framebuffer(&info->fbi); if (ret) { free(info); @@ -129,7 +147,7 @@ static int bcm2835fb_probe(struct device_d *dev) return 0; } -static struct driver_d bcm2835fb_driver = { +static struct driver bcm2835fb_driver = { .name = "bcm2835_fb", .probe = bcm2835fb_probe, }; diff --git a/drivers/video/bochs/Kconfig b/drivers/video/bochs/Kconfig new file mode 100644 index 0000000000..ae5d38a8ad --- /dev/null +++ b/drivers/video/bochs/Kconfig @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config DRIVER_VIDEO_BOCHS + select DRIVER_VIDEO_EDID + bool + +config DRIVER_VIDEO_BOCHS_PCI + bool "bochs dispi / QEMU standard VGA PCI driver" + select DRIVER_VIDEO_BOCHS + depends on PCI + help + Say yes here if you have a PCI VGA display controller that + implements the bochs dispi VBE extension. This is a very simple + interface to drive the graphical output of virtual machines + like bochs, VirtualBox and Qemu (-device VGA). + +config DRIVER_VIDEO_BOCHS_ISA + bool "bochs dispi / QEMU standard VGA ISA driver" + select DRIVER_VIDEO_BOCHS + help + Say yes here if you have a ISA (I/O ports) VGA display controller that + implements the bochs dispi VBE extension. This is a very simple + interface to drive the graphical output of virtual machines + like bochs, VirtualBox and Qemu (-device isa-vga). diff --git a/drivers/video/bochs/Makefile b/drivers/video/bochs/Makefile new file mode 100644 index 0000000000..3ab7ade8a1 --- /dev/null +++ b/drivers/video/bochs/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-y += bochs_hw.o +obj-$(CONFIG_DRIVER_VIDEO_BOCHS_PCI) += bochs_pci.o +obj-$(CONFIG_DRIVER_VIDEO_BOCHS_ISA) += bochs_isa.o diff --git a/drivers/video/bochs/bochs_hw.c b/drivers/video/bochs/bochs_hw.c new file mode 100644 index 0000000000..60439ddee5 --- /dev/null +++ b/drivers/video/bochs/bochs_hw.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2020 Ahmad Fatoum, Pengutronix +/* + * Driver for VGA with the Bochs VBE / QEMU stdvga extensions. + * + * Based on the Linux v5.11-rc1 bochs-dispi DRM driver. + */ + +#include <common.h> +#include <driver.h> +#include <fb.h> +#include "../edid.h" +#include "bochs_hw.h" + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa + +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +/* Offsets for accessing ioports via PCI BAR1 (MMIO) */ +#define VGA_MMIO_OFFSET (0x400 - 0x3c0) +#define VBE_MMIO_OFFSET 0x500 + +struct bochs { + struct fb_info fb; + void __iomem *fb_map; + void __iomem *mmio; +}; + +static void bochs_vga_writeb(struct bochs *bochs, u16 ioport, u8 val) +{ + if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df)) + return; + + if (bochs->mmio) { + int offset = ioport + VGA_MMIO_OFFSET; + writeb(val, bochs->mmio + offset); + } else { + outb(val, ioport); + } +} + +static u16 bochs_dispi_read(struct bochs *bochs, u16 reg) +{ + u16 ret = 0; + + if (bochs->mmio) { + int offset = VBE_MMIO_OFFSET + (reg << 1); + ret = readw(bochs->mmio + offset); + } else { + outw(reg, VBE_DISPI_IOPORT_INDEX); + ret = inw(VBE_DISPI_IOPORT_DATA); + } + return ret; +} + +static void bochs_dispi_write(struct bochs *bochs, u16 reg, u16 val) +{ + if (bochs->mmio) { + int offset = VBE_MMIO_OFFSET + (reg << 1); + writew(val, bochs->mmio + offset); + } else { + outw(reg, VBE_DISPI_IOPORT_INDEX); + outw(val, VBE_DISPI_IOPORT_DATA); + } +} + +static void bochs_fb_enable(struct fb_info *fb) +{ + struct bochs *bochs = fb->priv; + + bochs_vga_writeb(bochs, 0x3c0, 0x20); /* unblank */ + + bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, 0); + + bochs_dispi_write(bochs, VBE_DISPI_INDEX_BPP, fb->bits_per_pixel); + bochs_dispi_write(bochs, VBE_DISPI_INDEX_XRES, fb->xres); + bochs_dispi_write(bochs, VBE_DISPI_INDEX_YRES, fb->yres); + bochs_dispi_write(bochs, VBE_DISPI_INDEX_BANK, 0); + bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, fb->xres); + bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_HEIGHT, fb->yres); + bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, 0); + bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, 0); + + bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, + VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED ); +} + +static void bochs_fb_disable(struct fb_info *fb) +{ + struct bochs *bochs = fb->priv; + + bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, + bochs_dispi_read(bochs, VBE_DISPI_INDEX_ENABLE) & + ~VBE_DISPI_ENABLED); +} + +static struct fb_ops bochs_fb_ops = { + .fb_enable = bochs_fb_enable, + .fb_disable = bochs_fb_disable, +}; + +static int bochs_hw_load_edid(struct bochs *bochs) +{ + u8 *edid; + int i; + + edid = xzalloc(EDID_LENGTH); + + for (i = 0; i <= EDID_HEADER_END; i++) + edid[i] = readb(bochs->mmio + i); + + /* check header to detect whenever edid support is enabled in qemu */ + if (!edid_check_header(edid)) { + free(edid); + return -EILSEQ; + } + + for (i = EDID_HEADER_END + 1; i < EDID_LENGTH; i++) + edid[i] = readb(bochs->mmio + i); + + bochs->fb.edid_data = edid; + return 0; +} + +static int bochs_hw_read_version(struct bochs *bochs) +{ + u16 ver; + + ver = bochs_dispi_read(bochs, VBE_DISPI_INDEX_ID); + + if ((ver & 0xB0C0) != 0xB0C0) + return -ENODEV; + + return ver & 0xF; +} + +int bochs_hw_probe(struct device *dev, void __iomem *fb_map, + void __iomem *mmio) +{ + struct bochs *bochs; + struct fb_info *fb; + int ret; + + bochs = xzalloc(sizeof(*bochs)); + + bochs->fb_map = fb_map; + bochs->mmio = mmio; + + ret = bochs_hw_read_version(bochs); + if (ret < 0) { + free(bochs); + return ret; + } + + dev_info(dev, "detected bochs dispi v%u\n", ret); + + fb = &bochs->fb; + fb->screen_base = bochs->fb_map; + + fb->bits_per_pixel = 16; + fb->red.length = 5; + fb->green.length = 6; + fb->blue.length = 5; + fb->red.offset = 11; + fb->green.offset = 5; + fb->blue.offset = 0; + + /* EDID is only exposed over PCI */ + ret = -ENODEV; + + if (mmio) { + ret = bochs_hw_load_edid(bochs); + if (ret) + dev_warn(dev, "couldn't read EDID information\n"); + } + + if (ret) { + fb->mode = xzalloc(sizeof(*fb->mode)); + fb->modes.modes = fb->mode; + fb->modes.num_modes = 1; + + fb->mode->name = "640x480"; + fb->mode->xres = 640; + fb->mode->yres = 480; + } + + fb->priv = bochs; + fb->fbops = &bochs_fb_ops; + + fb->dev.parent = dev; + return register_framebuffer(fb); +} diff --git a/drivers/video/bochs/bochs_hw.h b/drivers/video/bochs/bochs_hw.h new file mode 100644 index 0000000000..c721113656 --- /dev/null +++ b/drivers/video/bochs/bochs_hw.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef BOCHS_HW_H +#define BOCHS_HW_H + +#include <linux/compiler.h> + +#define VBE_DISPI_IOPORT_INDEX 0x01CE +#define VBE_DISPI_IOPORT_DATA 0x01CF + +struct device; + +int bochs_hw_probe(struct device *dev, void __iomem *fb_map, void __iomem *mmio); + +#endif diff --git a/drivers/video/bochs/bochs_isa.c b/drivers/video/bochs/bochs_isa.c new file mode 100644 index 0000000000..50fdecd9c8 --- /dev/null +++ b/drivers/video/bochs/bochs_isa.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2020 Ahmad Fatoum, Pengutronix +/* + * ISA driver entry point for VGA with the Bochs VBE / QEMU stdvga extensions. + */ + +#include <common.h> +#include <driver.h> +#include <linux/ioport.h> +#include "bochs_hw.h" + +static int bochs_isa_detect(void) +{ + struct device *dev; + int ret; + + outw(0, VBE_DISPI_IOPORT_INDEX); + ret = inw(VBE_DISPI_IOPORT_DATA); + + if ((ret & 0xB0C0) != 0xB0C0) + return -ENODEV; + + dev = device_alloc("bochs-dispi", 0); + + ret = platform_device_register(dev); + if (ret) + return ret; + + return bochs_hw_probe(dev, IOMEM(0xe0000000), NULL); +} +device_initcall(bochs_isa_detect); diff --git a/drivers/video/bochs/bochs_pci.c b/drivers/video/bochs/bochs_pci.c new file mode 100644 index 0000000000..39c4d1b662 --- /dev/null +++ b/drivers/video/bochs/bochs_pci.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2020 Ahmad Fatoum, Pengutronix +/* + * PCI driver entry point for VGA with the Bochs VBE / QEMU stdvga extensions. + */ + +#include <common.h> +#include <driver.h> +#include <linux/pci.h> +#include "bochs_hw.h" + +static int bochs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + void __iomem *fb_map, *mmio; + int ret; + + ret = pci_enable_device(pdev); + if (ret) + return ret; + + fb_map = pci_iomap(pdev, 0); + mmio = pci_iomap(pdev, 2); + + return bochs_hw_probe(&pdev->dev, fb_map, mmio); +} + +static DEFINE_PCI_DEVICE_TABLE(bochs_pci_tbl) = { + /* https://github.com/qemu/qemu/blob/master/docs/specs/standard-vga.txt */ + { PCI_DEVICE(0x1234, 0x1111) }, + { }, +}; + +static struct pci_driver bochs_pci_driver = { + .name = "bochs-dispi", + .probe = bochs_pci_probe, + .id_table = bochs_pci_tbl, +}; +device_pci_driver(bochs_pci_driver); diff --git a/drivers/video/edid.c b/drivers/video/edid.c index bee4594118..7e6747ccd5 100644 --- a/drivers/video/edid.c +++ b/drivers/video/edid.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/video/edid.c * @@ -20,11 +21,6 @@ * * GTF Spreadsheet by Andy Morrish (1/5/97) * available at http://www.vesa.org - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - * */ #define pr_fmt(fmt) "EDID: " fmt @@ -223,19 +219,19 @@ static int edid_checksum(unsigned char *edid) return err; } -static int edid_check_header(unsigned char *edid) +bool edid_check_header(unsigned char *edid) { - int i, err = 1, fix = check_edid(edid); + int i, fix = check_edid(edid); if (fix) fix_edid(edid, fix); for (i = 0; i < 8; i++) { if (edid[i] != edid_v1_header[i]) - err = 0; + return false; } - return err; + return true; } /* @@ -851,17 +847,27 @@ edid_do_read_i2c(struct i2c_adapter *adapter, unsigned char *buf, ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers); } while (ret != xfers && --retries); - return ret == xfers ? 0 : -1; + if (ret == 0) + ret = -EPROTO; + + return ret == xfers ? 0 : ret; } void *edid_read_i2c(struct i2c_adapter *adapter) { u8 *block; + int ret; + + if (!IS_ENABLED(CONFIG_I2C)) + return NULL; block = xmalloc(EDID_LENGTH); - if (edid_do_read_i2c(adapter, block, 0, EDID_LENGTH)) + ret = edid_do_read_i2c(adapter, block, 0, EDID_LENGTH); + if (ret) { + dev_dbg(&adapter->dev, "EDID readout failed: %pe\n", ERR_PTR(ret)); goto out; + } return block; out: diff --git a/drivers/video/edid.h b/drivers/video/edid.h index 006d9f2834..b4bd578c96 100644 --- a/drivers/video/edid.h +++ b/drivers/video/edid.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * drivers/video/edid.h - EDID/DDC Header * @@ -9,11 +10,7 @@ * Ani Joshi <ajoshi@unixbox.com> * * DDC is a Trademark of VESA (Video Electronics Standard Association). - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. -*/ + */ #ifndef __EDID_H__ #define __EDID_H__ @@ -135,4 +132,14 @@ #define DPMS_SUSPEND (1 << 6) #define DPMS_STANDBY (1 << 7) +/** + * edid_check_header - sanity check the header of the base EDID block + * @raw_edid: pointer to raw base EDID block + * + * Sanity check the header of the base EDID block. + * + * Return: true if the header is perfect, false if any byte is wrong. + */ +bool edid_check_header(unsigned char *edid); + #endif /* __EDID_H__ */ diff --git a/drivers/video/efi_gop.c b/drivers/video/efi_gop.c index 7c083e4fb3..cd2506c04b 100644 --- a/drivers/video/efi_gop.c +++ b/drivers/video/efi_gop.c @@ -1,8 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2011 Intel Corporation; author Matt Fleming * Copyright (c) 2017 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * GPL v2 */ #include <common.h> @@ -13,7 +12,7 @@ #include <errno.h> #include <gui/graphic_utils.h> #include <efi.h> -#include <efi/efi.h> +#include <efi/efi-payload.h> #include <efi/efi-device.h> #define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0 @@ -64,7 +63,7 @@ struct efi_graphics_output_protocol { }; struct efi_gop_priv { - struct device_d *dev; + struct device *dev; struct fb_info fb; uint32_t mode; @@ -239,11 +238,23 @@ static int efi_gop_probe(struct efi_device *efidev) priv->fb.current_mode = priv->mode; ret = register_framebuffer(&priv->fb); - if (!ret) { - priv->dev->priv = &priv->fb; - return 0; + if (ret) + goto free_modes; + + priv->dev->priv = &priv->fb; + + if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE)) { + struct console_device *cdev; + + cdev = console_get_by_dev(&priv->fb.dev); + if (cdev) + dev_add_param_fixed(&cdev->class_dev, "linux.bootargs.earlycon", + "earlycon=efifb"); } + return 0; + +free_modes: if (priv->fb.modes.modes) { int i; diff --git a/drivers/video/fb.c b/drivers/video/fb.c index 2d82bc01fa..6f412d62c4 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <malloc.h> #include <fb.h> @@ -42,6 +43,12 @@ static int fb_close(struct cdev *cdev) return 0; } +void fb_damage(struct fb_info *info, struct fb_rect *rect) +{ + if (info->fbops->fb_damage) + info->fbops->fb_damage(info, rect); +} + static int fb_op_flush(struct cdev *cdev) { struct fb_info *info = cdev->priv; @@ -119,22 +126,25 @@ int fb_disable(struct fb_info *info) return 0; } +static int fb_enable_get(struct param_d *param, void *priv) +{ + struct fb_info *info = priv; + + info->p_enable = info->enabled; + return 0; +} + static int fb_enable_set(struct param_d *param, void *priv) { struct fb_info *info = priv; - int enable; if (!info->mode) return -EINVAL; - enable = info->p_enable; - - if (enable) - fb_enable(info); + if (info->p_enable) + return fb_enable(info); else - fb_disable(info); - - return 0; + return fb_disable(info); } static struct fb_videomode *fb_num_to_mode(struct fb_info *info, int num) @@ -154,7 +164,7 @@ static struct fb_videomode *fb_num_to_mode(struct fb_info *info, int num) static int fb_setup_mode(struct fb_info *info) { - struct device_d *dev = &info->dev; + struct device *dev = &info->dev; int ret; struct fb_videomode *mode; @@ -247,7 +257,7 @@ static void fb_print_modes(struct display_timings *modes) fb_print_mode(&modes->modes[i]); } -static void fb_info(struct device_d *dev) +static void fb_info(struct device *dev) { struct fb_info *info = dev->priv; @@ -275,7 +285,7 @@ static int fb_set_shadowfb(struct param_d *p, void *priv) int register_framebuffer(struct fb_info *info) { int id = get_free_deviceid("fb"); - struct device_d *dev; + struct device *dev; int ret, num_modes, i; const char **names; @@ -314,7 +324,7 @@ int register_framebuffer(struct fb_info *info) if (ret) goto err_free; - dev_add_param_bool(dev, "enable", fb_enable_set, NULL, + dev_add_param_bool(dev, "enable", fb_enable_set, fb_enable_get, &info->p_enable, info); if (IS_ENABLED(CONFIG_DRIVER_VIDEO_EDID)) diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c index b261f17048..6c85e8e06a 100644 --- a/drivers/video/fbconsole.c +++ b/drivers/video/fbconsole.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <errno.h> #include <malloc.h> @@ -17,6 +18,12 @@ enum state_t { struct fbc_priv { struct console_device cdev; struct fb_info *fb; + struct { + u32 top; + u32 left; + u32 bottom; + u32 right; + } margin; struct screen *sc; @@ -59,9 +66,26 @@ static int fbc_tstc(struct console_device *cdev) static void cls(struct fbc_priv *priv) { void *buf = gui_screen_render_buffer(priv->sc); + struct fb_info *fb = priv->fb; + int width = fb->xres - priv->margin.left - priv->margin.right; + int height = fb->yres - priv->margin.top - priv->margin.bottom; + void *adr; + + adr = buf + priv->fb->line_length * priv->margin.top; + + if (!priv->margin.left && !priv->margin.right) { + memset(adr, 0, priv->fb->line_length * height); + } else { + int bpp = priv->fb->bits_per_pixel >> 3; + int y; - memset(buf, 0, priv->fb->line_length * priv->fb->yres); - gu_screen_blit(priv->sc); + for (y = 0; y < height; y++) { + memset(adr + priv->margin.left * bpp, 0, width * bpp); + adr += priv->fb->line_length; + } + } + gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top, + width, height); } struct rgb { @@ -121,7 +145,8 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c) uint8_t t = inbuf[i]; int j; - adr = buf + line_length * (y * priv->font->height + i) + x * priv->font->width * bpp; + adr = buf + line_length * (priv->margin.top + y * priv->font->height + i) + + (priv->margin.left + x * priv->font->width) * bpp; for (j = 0; j < priv->font->width; j++) { if (t & 0x80) @@ -141,9 +166,11 @@ static void video_invertchar(struct fbc_priv *priv, int x, int y) buf = gui_screen_render_buffer(priv->sc); - gu_invert_area(priv->fb, buf, x * priv->font->width, y * priv->font->height, + gu_invert_area(priv->fb, buf, priv->margin.left + x * priv->font->width, + priv->margin.top + y * priv->font->height, priv->font->width, priv->font->height); - gu_screen_blit_area(priv->sc, x * priv->font->width, y * priv->font->height, + gu_screen_blit_area(priv->sc, priv->margin.left + x * priv->font->width, + priv->margin.top + y * priv->font->height, priv->font->width, priv->font->height); } @@ -184,8 +211,9 @@ static void printchar(struct fbc_priv *priv, int c) default: drawchar(priv, priv->x, priv->y, c); - gu_screen_blit_area(priv->sc, priv->x * priv->font->width, - priv->y * priv->font->height, + gu_screen_blit_area(priv->sc, + priv->margin.left + priv->x * priv->font->width, + priv->margin.top + priv->y * priv->font->height, priv->font->width, priv->font->height); priv->x++; @@ -197,15 +225,36 @@ static void printchar(struct fbc_priv *priv, int c) if (priv->y > priv->rows) { void *buf; + void *adr; u32 line_length = priv->fb->line_length; int line_height = line_length * priv->font->height; + int width = priv->fb->xres - priv->margin.left - priv->margin.right; + int height = (priv->rows + 1) * priv->font->height; buf = gui_screen_render_buffer(priv->sc); + adr = buf + priv->margin.top * line_length; + + if (!priv->margin.left && !priv->margin.right) { + memcpy(adr, adr + line_height, line_height * priv->rows); + memset(adr + line_height * priv->rows, 0, line_height); + } else { + int bpp = priv->fb->bits_per_pixel >> 3; + int y; + + adr += priv->margin.left * bpp; + + for (y = 0; y < height - priv->font->height; y++) { + memcpy(adr, adr + line_height, width * bpp); + adr += line_length; + } + for (y = height - priv->font->height; y < height; y++) { + memset(adr, 0, width * bpp); + adr += line_length; + } + } - memcpy(buf, buf + line_height, line_height * priv->rows); - memset(buf + line_height * priv->rows, 0, line_height); - - gu_screen_blit(priv->sc); + gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top, + width, height); priv->y = priv->rows; } @@ -400,8 +449,9 @@ static void fbc_putc(struct console_device *cdev, char c) static int setup_font(struct fbc_priv *priv) { - struct fb_info *fb = priv->fb; const struct font_desc *font; + unsigned int height = priv->fb->yres - priv->margin.top - priv->margin.bottom; + unsigned int width = priv->fb->xres - priv->margin.left - priv->margin.right; font = find_font_enum(priv->par_font_val); if (!font) { @@ -410,8 +460,8 @@ static int setup_font(struct fbc_priv *priv) priv->font = font; - priv->rows = fb->yres / priv->font->height - 1; - priv->cols = fb->xres / priv->font->width - 1; + priv->rows = height / priv->font->height - 1; + priv->cols = width / priv->font->width - 1; return 0; } @@ -471,6 +521,35 @@ static int set_font(struct param_d *p, void *vpriv) return 0; } +static int set_margin(struct param_d *p, void *vpriv) +{ + struct fbc_priv *priv = vpriv; + struct console_device *cdev = &priv->cdev; + int ret; + + if (!priv->font) { + ret = setup_font(priv); + if (ret) + return ret; + } + + priv->margin.left = min(priv->margin.left, + priv->fb->xres - priv->margin.right - priv->font->width); + priv->margin.top = min(priv->margin.top, + priv->fb->yres - priv->margin.bottom - priv->font->height); + priv->margin.right = min(priv->margin.right, + priv->fb->xres - priv->margin.left - priv->font->width); + priv->margin.bottom = min(priv->margin.bottom, + priv->fb->yres - priv->margin.top - priv->font->height); + + if (cdev->f_active & (CONSOLE_STDOUT | CONSOLE_STDERR)) { + cls(priv); + setup_font(priv); + } + + return 0; +} + int register_fbconsole(struct fb_info *fb) { struct fbc_priv *priv; @@ -507,6 +586,15 @@ int register_fbconsole(struct fb_info *fb) set_font, NULL, &priv->par_font_val, priv); + dev_add_param_uint32(&cdev->class_dev, "margin.top", set_margin, + NULL, &priv->margin.top, "%u", priv); + dev_add_param_uint32(&cdev->class_dev, "margin.left", set_margin, + NULL, &priv->margin.left, "%u", priv); + dev_add_param_uint32(&cdev->class_dev, "margin.bottom", set_margin, + NULL, &priv->margin.bottom, "%u", priv); + dev_add_param_uint32(&cdev->class_dev, "margin.right", set_margin, + NULL, &priv->margin.right, "%u", priv); + pr_info("registered as %s%d\n", cdev->class_dev.name, cdev->class_dev.id); return 0; diff --git a/drivers/video/imx-ipu-fb.c b/drivers/video/imx-ipu-fb.c index 9cc7a911ea..e2ff01929b 100644 --- a/drivers/video/imx-ipu-fb.c +++ b/drivers/video/imx-ipu-fb.c @@ -1,34 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2009 * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <common.h> #include <dma.h> #include <init.h> #include <io.h> -#include <mach/imx35-regs.h> +#include <mach/imx/imx35-regs.h> #include <fb.h> -#include <mach/imxfb.h> +#include <platform_data/imxfb.h> #include <malloc.h> #include <errno.h> -#include <asm-generic/div64.h> +#include <linux/math64.h> #include <mmu.h> -#include <mach/imx-ipu-fb.h> +#include <mach/imx/imx-ipu-fb.h> #include <linux/clk.h> #include <linux/err.h> @@ -42,7 +29,7 @@ struct ipu_fb_info { struct fb_info info; struct fb_info overlay; - struct device_d *dev; + struct device *dev; unsigned int alpha; int disable_fractional_divider; @@ -972,6 +959,7 @@ static int sdc_fb_register_overlay(struct ipu_fb_info *fbi, void *fb) sdc_enable_channel(fbi, overlay->screen_base, IDMAC_SDC_1); + fbi->overlay.dev.parent = &fbi->info.dev; ret = register_framebuffer(&fbi->overlay); if (ret < 0) { dev_err(fbi->dev, "failed to register framebuffer\n"); @@ -986,7 +974,7 @@ static int sdc_fb_register_overlay(struct ipu_fb_info *fbi, void *fb) #endif -static int imxfb_probe(struct device_d *dev) +static int imxfb_probe(struct device *dev) { struct resource *iores; struct ipu_fb_info *fbi; @@ -1042,6 +1030,7 @@ static int imxfb_probe(struct device_d *dev) sdc_enable_channel(fbi, info->screen_base, IDMAC_SDC_0); + fbi->info.dev.parent = dev; ret = register_framebuffer(&fbi->info); if (ret < 0) { dev_err(dev, "failed to register framebuffer\n"); @@ -1054,7 +1043,7 @@ static int imxfb_probe(struct device_d *dev) return ret; } -static struct driver_d imx3fb_driver = { +static struct driver imx3fb_driver = { .name = "imx-ipu-fb", .probe = imxfb_probe, }; diff --git a/drivers/video/imx-ipu-v3/Kconfig b/drivers/video/imx-ipu-v3/Kconfig index 55aeac9415..4429e815cd 100644 --- a/drivers/video/imx-ipu-v3/Kconfig +++ b/drivers/video/imx-ipu-v3/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config DRIVER_VIDEO_IMX_IPUV3 bool "i.MX IPUv3 driver" depends on ARCH_IMX diff --git a/drivers/video/imx-ipu-v3/Makefile b/drivers/video/imx-ipu-v3/Makefile index 1f6812021e..709a9203a3 100644 --- a/drivers/video/imx-ipu-v3/Makefile +++ b/drivers/video/imx-ipu-v3/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += ipu-common.o ipu-dmfc.o ipu-di.o obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += ipu-dp.o ipuv3-plane.o ipufb.o obj-$(CONFIG_DRIVER_VIDEO_IMX_IPUV3) += ipu-dc.o diff --git a/drivers/video/imx-ipu-v3/imx-hdmi.c b/drivers/video/imx-ipu-v3/imx-hdmi.c index 17b6e4cc25..2d5fd98666 100644 --- a/drivers/video/imx-ipu-v3/imx-hdmi.c +++ b/drivers/video/imx-ipu-v3/imx-hdmi.c @@ -1,14 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * * Designware High-Definition Multimedia Interface (HDMI) driver * * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. */ #include <common.h> #include <fb.h> @@ -19,13 +14,12 @@ #include <init.h> #include <linux/clk.h> #include <linux/err.h> -#include <asm-generic/div64.h> -#include <linux/clk.h> +#include <linux/math64.h> #include <i2c/i2c.h> #include <video/media-bus-format.h> #include <video/vpl.h> -#include <mach/imx6-regs.h> -#include <mach/imx53-regs.h> +#include <mach/imx/imx6-regs.h> +#include <mach/imx/imx53-regs.h> #include "imx-ipu-v3.h" #include "ipuv3-plane.h" @@ -119,7 +113,7 @@ struct hdmi_data_info { struct dw_hdmi { enum dw_hdmi_devtype dev_type; - struct device_d *dev; + struct device *dev; struct clk *isfr_clk; struct clk *iahb_clk; @@ -1083,19 +1077,18 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) /* Workaround to clear the overflow condition */ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) { - int count; + int count = 4; u8 val; /* TMDS software reset */ hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); - if (hdmi->dev_type == IMX6DL_HDMI) { - hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); - return; - } - for (count = 0; count < 4; count++) + if (hdmi->dev_type == IMX6DL_HDMI) + count = 1; + + while (count--) hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); } @@ -1193,32 +1186,18 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi) hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE); } -struct dw_hdmi_data { - unsigned ipu_mask; - enum dw_hdmi_devtype devtype; -}; - -static struct dw_hdmi_data imx6q_hdmi_data = { - .ipu_mask = 0xf, - .devtype = IMX6Q_HDMI, -}; - -static struct dw_hdmi_data imx6dl_hdmi_data = { - .ipu_mask = 0x3, - .devtype = IMX6DL_HDMI, -}; - static struct of_device_id dw_hdmi_dt_ids[] = { { .compatible = "fsl,imx6q-hdmi", - .data = &imx6q_hdmi_data, + .data = (void *)IMX6Q_HDMI, }, { .compatible = "fsl,imx6dl-hdmi", - .data = &imx6dl_hdmi_data, + .data = (void *)IMX6DL_HDMI, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, dw_hdmi_dt_ids); static int dw_hdmi_get_modes(struct dw_hdmi *hdmi, struct display_timings *timings) { @@ -1270,17 +1249,12 @@ static int dw_hdmi_ioctl(struct vpl *vpl, unsigned int port, return 0; } -static int dw_hdmi_probe(struct device_d *dev) +static int dw_hdmi_probe(struct device *dev) { struct resource *iores; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct dw_hdmi *hdmi; int ret; - const struct dw_hdmi_data *devtype; - - ret = dev_get_drvdata(dev, (const void **)&devtype); - if (ret) - return ret; hdmi = xzalloc(sizeof(*hdmi)); @@ -1289,9 +1263,7 @@ static int dw_hdmi_probe(struct device_d *dev) hdmi->sample_rate = 48000; hdmi->ratio = 100; - ret = dev_get_drvdata(dev, (const void **)&hdmi->dev_type); - if (ret) - return ret; + hdmi->dev_type = (enum dw_hdmi_devtype)device_get_match_data(dev); hdmi->ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); @@ -1376,7 +1348,7 @@ err_isfr: return ret; } -static struct driver_d dw_hdmi_driver = { +static struct driver dw_hdmi_driver = { .probe = dw_hdmi_probe, .of_compatible = dw_hdmi_dt_ids, .name = "imx-hdmi", diff --git a/drivers/video/imx-ipu-v3/imx-hdmi.h b/drivers/video/imx-ipu-v3/imx-hdmi.h index b3e144227f..dcffe75529 100644 --- a/drivers/video/imx-ipu-v3/imx-hdmi.h +++ b/drivers/video/imx-ipu-v3/imx-hdmi.h @@ -1,10 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2011 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __IMX_HDMI_H__ diff --git a/drivers/video/imx-ipu-v3/imx-ipu-v3.h b/drivers/video/imx-ipu-v3/imx-ipu-v3.h index cdfff6992f..8b78b716d3 100644 --- a/drivers/video/imx-ipu-v3/imx-ipu-v3.h +++ b/drivers/video/imx-ipu-v3/imx-ipu-v3.h @@ -1,12 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright 2005-2009 Freescale Semiconductor, Inc. - * - * The code contained herein is licensed under the GNU Lesser General - * Public License. You may obtain a copy of the GNU Lesser General - * Public License Version 2.1 or later at the following locations: - * - * http://www.opensource.org/licenses/lgpl-license.html - * http://www.gnu.org/copyleft/lgpl.html */ #ifndef __DRM_IPU_H__ diff --git a/drivers/video/imx-ipu-v3/imx-ldb.c b/drivers/video/imx-ipu-v3/imx-ldb.c index 9b4524274c..3ed6d44f5b 100644 --- a/drivers/video/imx-ipu-v3/imx-ldb.c +++ b/drivers/video/imx-ipu-v3/imx-ldb.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * i.MX drm driver - parallel display implementation * * Copyright (C) 2012 Sascha Hauer, Pengutronix - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. */ #include <common.h> @@ -31,10 +18,9 @@ #include <mfd/imx6q-iomuxc-gpr.h> #include <linux/clk.h> #include <linux/err.h> -#include <asm-generic/div64.h> -#include <linux/clk.h> -#include <mach/imx6-regs.h> -#include <mach/imx53-regs.h> +#include <linux/math64.h> +#include <mach/imx/imx6-regs.h> +#include <mach/imx/imx53-regs.h> #include "imx-ipu-v3.h" #include "ipuv3-plane.h" @@ -75,7 +61,7 @@ struct imx_ldb_data { }; struct imx_ldb { - struct device_d *dev; + struct device *dev; u32 bus_format; int mode_valid; struct imx_ldb_channel channel[2]; @@ -162,7 +148,7 @@ static int imx6q_set_clock(struct imx_ldb *ldb, int ipuno, int dino, int chno, u diclk = clk_lookup(clkname); free(clkname); if (IS_ERR(diclk)) { - dev_err(ldb->dev, "failed to get di clk: %s\n", strerrorp(diclk)); + dev_err(ldb->dev, "failed to get di clk: %pe\n", diclk); return PTR_ERR(diclk); } @@ -170,7 +156,7 @@ static int imx6q_set_clock(struct imx_ldb *ldb, int ipuno, int dino, int chno, u ldbclk = clk_lookup(clkname); free(clkname); if (IS_ERR(ldbclk)) { - dev_err(ldb->dev, "failed to get ldb clk: %s\n", strerrorp(ldbclk)); + dev_err(ldb->dev, "failed to get ldb clk: %pe\n", ldbclk); return PTR_ERR(ldbclk); } @@ -233,7 +219,7 @@ static int imx53_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di, diclk = clk_lookup(clkname); free(clkname); if (IS_ERR(diclk)) { - dev_err(ldb->dev, "failed to get di clk: %s\n", strerrorp(diclk)); + dev_err(ldb->dev, "failed to get di clk: %pe\n", diclk); return PTR_ERR(diclk); } @@ -241,7 +227,7 @@ static int imx53_ldb_prepare(struct imx_ldb_channel *imx_ldb_ch, int di, ldbclk = clk_lookup(clkname); free(clkname); if (IS_ERR(ldbclk)) { - dev_err(ldb->dev, "failed to get ldb clk: %s\n", strerrorp(ldbclk)); + dev_err(ldb->dev, "failed to get ldb clk: %pe\n", ldbclk); return PTR_ERR(ldbclk); } @@ -316,9 +302,9 @@ static int imx_ldb_ioctl(struct vpl *vpl, unsigned int port, return 0; } -static int imx_ldb_probe(struct device_d *dev) +static int imx_ldb_probe(struct device *dev) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct device_node *child; struct imx_ldb *imx_ldb; int ret, i; @@ -417,8 +403,9 @@ static struct of_device_id imx_ldb_dt_ids[] = { { .compatible = "fsl,imx53-ldb", &imx_ldb_data_imx53}, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids); -static struct driver_d imx_ldb_driver = { +static struct driver imx_ldb_driver = { .probe = imx_ldb_probe, .of_compatible = imx_ldb_dt_ids, .name = "imx-ldb", diff --git a/drivers/video/imx-ipu-v3/imx-pd.c b/drivers/video/imx-ipu-v3/imx-pd.c index 601be35880..d8b5f90a6c 100644 --- a/drivers/video/imx-ipu-v3/imx-pd.c +++ b/drivers/video/imx-ipu-v3/imx-pd.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * i.MX drm driver - parallel display implementation * * Copyright (C) 2016 Philippe Leduc - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * 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 <common.h> @@ -27,8 +19,10 @@ #include "imx-ipu-v3.h" +#define IMX_PD_OUTPUT_PORT 1 + struct imx_pd { - struct device_d *dev; + struct device *dev; struct display_timings *timings; u32 bus_format; struct vpl vpl; @@ -39,7 +33,6 @@ static int imx_pd_ioctl(struct vpl *vpl, unsigned int port, { struct imx_pd *imx_pd = container_of(vpl, struct imx_pd, vpl); struct ipu_di_mode *mode; - struct display_timings *timings; switch (cmd) { case IMX_IPU_VPL_DI_MODE: @@ -50,22 +43,29 @@ static int imx_pd_ioctl(struct vpl *vpl, unsigned int port, return 0; case VPL_GET_VIDEOMODES: - timings = data; - - timings->num_modes = imx_pd->timings->num_modes; - timings->native_mode = imx_pd->timings->native_mode; - timings->modes = imx_pd->timings->modes; - timings->edid = NULL; - return 0; + if (imx_pd->timings) { + struct display_timings *timings = data; + + timings->num_modes = imx_pd->timings->num_modes; + timings->native_mode = imx_pd->timings->native_mode; + timings->modes = imx_pd->timings->modes; + timings->edid = NULL; + return 0; + } + break; } + if (!imx_pd->timings) + return vpl_ioctl(vpl, IMX_PD_OUTPUT_PORT, cmd, data); + return 0; } -static int imx_pd_probe(struct device_d *dev) +static int imx_pd_probe(struct device *dev) { - struct device_node *node = dev->device_node; + struct device_node *node = dev->of_node; struct imx_pd *imx_pd; + struct device_node *port; const char *fmt; int ret; @@ -88,8 +88,11 @@ static int imx_pd_probe(struct device_d *dev) imx_pd->timings = of_get_display_timings(node); if (!imx_pd->timings) { - dev_err(dev, "No display timings panel found\n"); - return -EINVAL; + port = of_graph_get_port_by_id(node, IMX_PD_OUTPUT_PORT); + if (!port) { + dev_err(dev, "Neither display timings in nor remote panel found in node\n"); + return -EINVAL; + } } imx_pd->vpl.node = node; @@ -105,8 +108,9 @@ static struct of_device_id imx_pd_dt_ids[] = { { .compatible = "fsl,imx-parallel-display", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_pd_dt_ids); -static struct driver_d imx_pd_driver = { +static struct driver imx_pd_driver = { .probe = imx_pd_probe, .of_compatible = imx_pd_dt_ids, .name = "imx-parallel-display", diff --git a/drivers/video/imx-ipu-v3/ipu-common.c b/drivers/video/imx-ipu-v3/ipu-common.c index 1811e50227..4909119d87 100644 --- a/drivers/video/imx-ipu-v3/ipu-common.c +++ b/drivers/video/imx-ipu-v3/ipu-common.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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 <common.h> @@ -20,10 +11,10 @@ #include <driver.h> #include <init.h> #include <linux/mutex.h> -#include <mach/generic.h> -#include <mach/imx6-regs.h> -#include <mach/imx53-regs.h> -#include <mach/imx51-regs.h> +#include <mach/imx/generic.h> +#include <mach/imx/imx6-regs.h> +#include <mach/imx/imx53-regs.h> +#include <mach/imx/imx51-regs.h> #include "imx-ipu-v3.h" #include "ipu-prv.h" @@ -618,9 +609,10 @@ static struct of_device_id imx_ipu_dt_ids[] = { { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); static int ipu_submodules_init(struct ipu_soc *ipu, - struct device_d *dev, void __iomem *ipu_base, + struct device *dev, void __iomem *ipu_base, struct clk *ipu_clk) { char *unit; @@ -714,16 +706,16 @@ static struct ipu_platform_reg client_reg[] = { static int ipu_client_id; -static int ipu_add_subdevice_pdata(struct device_d *ipu_dev, - struct ipu_platform_reg *reg) +static int ipu_add_subdevice_pdata(struct device *ipu_dev, + struct ipu_platform_reg *reg) { - struct device_d *dev; + struct device *dev; int ret; dev = device_alloc(reg->name, ipu_client_id++); dev->parent = ipu_dev; device_add_data(dev, ®->pdata, sizeof(reg->pdata)); - ((struct ipu_client_platformdata *)dev->platform_data)->device_node = ipu_dev->device_node; + ((struct ipu_client_platformdata *)dev->platform_data)->device_node = ipu_dev->of_node; ret = platform_device_register(dev); @@ -749,7 +741,7 @@ err_register: return ret; } -static int ipu_probe(struct device_d *dev) +static int ipu_probe(struct device *dev) { struct resource *iores; struct ipu_soc *ipu; @@ -804,9 +796,8 @@ static int ipu_probe(struct device_d *dev) ipu->clk = clk_get(dev, "bus"); if (IS_ERR(ipu->clk)) { - ret = PTR_ERR(ipu->clk); - dev_err(dev, "clk_get failed: %s\n", strerror(-ret)); - return ret; + dev_err(dev, "clk_get failed: %pe\n", ipu->clk); + return PTR_ERR(ipu->clk); } dev->priv = ipu; @@ -854,7 +845,7 @@ out_failed_reset: return ret; } -static struct driver_d imx_ipu_driver = { +static struct driver imx_ipu_driver = { .name = "imx-ipuv3", .of_compatible = imx_ipu_dt_ids, .probe = ipu_probe, diff --git a/drivers/video/imx-ipu-v3/ipu-dc.c b/drivers/video/imx-ipu-v3/ipu-dc.c index 7b343e8149..a0292fc4a1 100644 --- a/drivers/video/imx-ipu-v3/ipu-dc.c +++ b/drivers/video/imx-ipu-v3/ipu-dc.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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 <common.h> @@ -105,7 +96,7 @@ struct ipu_dc_priv { void __iomem *dc_reg; void __iomem *dc_tmpl_reg; struct ipu_soc *ipu; - struct device_d *dev; + struct device *dev; struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; }; @@ -321,7 +312,7 @@ void ipu_dc_put(struct ipu_dc *dc) } EXPORT_SYMBOL_GPL(ipu_dc_put); -int ipu_dc_init(struct ipu_soc *ipu, struct device_d *dev, +int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base, void __iomem *template_base) { struct ipu_dc_priv *priv; diff --git a/drivers/video/imx-ipu-v3/ipu-di.c b/drivers/video/imx-ipu-v3/ipu-di.c index b4302412e0..6156911bfc 100644 --- a/drivers/video/imx-ipu-v3/ipu-di.c +++ b/drivers/video/imx-ipu-v3/ipu-di.c @@ -1,21 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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 <common.h> #include <linux/err.h> #include <linux/clk.h> -#include <asm-generic/div64.h> +#include <linux/math64.h> #include <malloc.h> #include "imx-ipu-v3.h" @@ -167,9 +158,10 @@ static int ipu_di_clk_calc_div(unsigned long inrate, unsigned long outrate) return div; } -static unsigned long clk_di_recalc_rate(struct clk *clk, +static unsigned long clk_di_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { + struct clk *clk = clk_hw_to_clk(hw); struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel); unsigned long outrate; u32 div = ipu_di_read(di, DI_BS_CLKGEN0); @@ -182,9 +174,10 @@ static unsigned long clk_di_recalc_rate(struct clk *clk, return outrate; } -static long clk_di_round_rate(struct clk *clk, unsigned long rate, +static long clk_di_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { + struct clk *clk = clk_hw_to_clk(hw); struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel); unsigned long outrate; int div; @@ -206,9 +199,10 @@ static long clk_di_round_rate(struct clk *clk, unsigned long rate, return outrate; } -static int clk_di_set_rate(struct clk *clk, unsigned long rate, +static int clk_di_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { + struct clk *clk = clk_hw_to_clk(hw); struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel); int div; u32 clkgen0; @@ -224,8 +218,9 @@ static int clk_di_set_rate(struct clk *clk, unsigned long rate, return 0; } -static int clk_di_get_parent(struct clk *clk) +static int clk_di_get_parent(struct clk_hw *hw) { + struct clk *clk = clk_hw_to_clk(hw); struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel); u32 val; @@ -234,8 +229,9 @@ static int clk_di_get_parent(struct clk *clk) return val & DI_GEN_DI_CLK_EXT ? 1 : 0; } -static int clk_di_set_parent(struct clk *clk, u8 index) +static int clk_di_set_parent(struct clk_hw *hw, u8 index) { + struct clk *clk = clk_hw_to_clk(hw); struct ipu_di *di = container_of(clk, struct ipu_di, clk_di_pixel); u32 val; @@ -704,7 +700,7 @@ void ipu_di_put(struct ipu_di *di) } EXPORT_SYMBOL_GPL(ipu_di_put); -int ipu_di_init(struct ipu_soc *ipu, struct device_d *dev, int id, +int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, void __iomem *base, u32 module, struct clk *clk_ipu) { @@ -740,7 +736,7 @@ int ipu_di_init(struct ipu_soc *ipu, struct device_d *dev, int id, di->clk_di_pixel.ops = &clk_di_ops; di->clk_di_pixel.num_parents = 2; di->clk_di_pixel.name = di->clk_name; - ret = clk_register(&di->clk_di_pixel); + ret = bclk_register(&di->clk_di_pixel); if (ret) goto failed_clk_register; diff --git a/drivers/video/imx-ipu-v3/ipu-dmfc.c b/drivers/video/imx-ipu-v3/ipu-dmfc.c index 29c4bb4780..4be6a15eef 100644 --- a/drivers/video/imx-ipu-v3/ipu-dmfc.c +++ b/drivers/video/imx-ipu-v3/ipu-dmfc.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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 <common.h> @@ -109,7 +100,7 @@ struct dmfc_channel { struct ipu_dmfc_priv { struct ipu_soc *ipu; - struct device_d *dev; + struct device *dev; struct dmfc_channel channels[DMFC_NUM_CHANNELS]; unsigned long bandwidth_per_slot; void __iomem *base; @@ -349,8 +340,8 @@ void ipu_dmfc_put(struct dmfc_channel *dmfc) } EXPORT_SYMBOL_GPL(ipu_dmfc_put); -int ipu_dmfc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base, - struct clk *ipu_clk) +int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base, + struct clk *ipu_clk) { struct ipu_dmfc_priv *priv; int i; diff --git a/drivers/video/imx-ipu-v3/ipu-dp.c b/drivers/video/imx-ipu-v3/ipu-dp.c index 8829954db0..68b45c11c7 100644 --- a/drivers/video/imx-ipu-v3/ipu-dp.c +++ b/drivers/video/imx-ipu-v3/ipu-dp.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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 <common.h> #include <linux/err.h> @@ -65,7 +56,7 @@ struct ipu_flow { struct ipu_dp_priv { struct ipu_soc *ipu; - struct device_d *dev; + struct device *dev; void __iomem *base; struct ipu_flow flow[IPUV3_NUM_FLOWS]; int use_count; @@ -286,7 +277,7 @@ void ipu_dp_put(struct ipu_dp *dp) } EXPORT_SYMBOL_GPL(ipu_dp_put); -int ipu_dp_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base) +int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base) { struct ipu_dp_priv *priv; int i; diff --git a/drivers/video/imx-ipu-v3/ipu-prv.h b/drivers/video/imx-ipu-v3/ipu-prv.h index 4d1c0692de..4465711ee4 100644 --- a/drivers/video/imx-ipu-v3/ipu-prv.h +++ b/drivers/video/imx-ipu-v3/ipu-prv.h @@ -1,16 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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 __IPU_PRV_H__ #define __IPU_PRV_H__ @@ -149,7 +140,7 @@ struct ipu_di; struct ipu_devtype; struct ipu_soc { - struct device_d *dev; + struct device *dev; const struct ipu_devtype *devtype; enum ipuv3_type ipu_type; spinlock_t lock; @@ -181,22 +172,23 @@ void ipu_srm_dp_sync_update(struct ipu_soc *ipu); int ipu_module_enable(struct ipu_soc *ipu, u32 mask); int ipu_module_disable(struct ipu_soc *ipu, u32 mask); -int ipu_di_init(struct ipu_soc *ipu, struct device_d *dev, int id, +int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, void __iomem *base, u32 module, struct clk *ipu_clk); void ipu_di_exit(struct ipu_soc *ipu, int id); -int ipu_dmfc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base, - struct clk *ipu_clk); +int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base, + struct clk *ipu_clk); void ipu_dmfc_exit(struct ipu_soc *ipu); -int ipu_dp_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base); +int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base); void ipu_dp_exit(struct ipu_soc *ipu); -int ipu_dc_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base, +int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, void __iomem *base, void __iomem *template_base); void ipu_dc_exit(struct ipu_soc *ipu); -int ipu_cpmem_init(struct ipu_soc *ipu, struct device_d *dev, void __iomem *base); +int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, + void __iomem *base); void ipu_cpmem_exit(struct ipu_soc *ipu); #endif /* __IPU_PRV_H__ */ diff --git a/drivers/video/imx-ipu-v3/ipufb.c b/drivers/video/imx-ipu-v3/ipufb.c index 683f298e76..e4ac988053 100644 --- a/drivers/video/imx-ipu-v3/ipufb.c +++ b/drivers/video/imx-ipu-v3/ipufb.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Freescale i.MX Frame Buffer device driver * * Copyright (C) 2004 Sascha Hauer, Pengutronix * Based on acornfb.c Copyright (C) Russell King. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - * */ #define pr_fmt(fmt) "IPU: " fmt @@ -22,7 +18,7 @@ #include <of_graph.h> #include <linux/clk.h> #include <linux/err.h> -#include <asm-generic/div64.h> +#include <linux/math64.h> #include <video/media-bus-format.h> #include "imx-ipu-v3.h" @@ -49,7 +45,7 @@ struct ipufb_info { struct fb_videomode *mode; struct fb_info info; - struct device_d *dev; + struct device *dev; /* plane[0] is the full plane, plane[1] is the partial plane */ struct ipu_plane *plane[2]; @@ -111,10 +107,8 @@ static int ipu_crtc_mode_set(struct ipufb_info *fbi, struct ipu_di_mode di_mode = {}; u32 bus_format = 0; - dev_info(fbi->dev, "%s: mode->xres: %d\n", __func__, - mode->xres); - dev_info(fbi->dev, "%s: mode->yres: %d\n", __func__, - mode->yres); + dev_info(fbi->dev, "%s: mode->xres: %d mode->yres: %d\n", __func__, + mode->xres, mode->yres); vpl_ioctl(&fbi->vpl, 2 + fbi->dino, IMX_IPU_VPL_DI_MODE, &di_mode); vpl_ioctl(&fbi->vpl, 2 + fbi->dino, VPL_GET_BUS_FORMAT, &bus_format); @@ -275,7 +269,7 @@ err_out: return ret; } -static int ipufb_probe(struct device_d *dev) +static int ipufb_probe(struct device *dev) { struct ipufb_info *fbi; struct fb_info *info; @@ -288,7 +282,7 @@ static int ipufb_probe(struct device_d *dev) fbi = xzalloc(sizeof(*fbi)); info = &fbi->info; - ipuid = of_alias_get_id(dev->parent->device_node, "ipu"); + ipuid = of_alias_get_id(dev->parent->of_node, "ipu"); fbi->name = basprintf("ipu%d-di%d", ipuid + 1, pdata->di); fbi->id = ipuid * 2 + pdata->di; fbi->dino = pdata->di; @@ -311,11 +305,12 @@ static int ipufb_probe(struct device_d *dev) if (ret) return ret; - node = of_graph_get_port_by_id(dev->parent->device_node, 2 + pdata->di); + node = of_graph_get_port_by_id(dev->parent->of_node, 2 + pdata->di); if (node && of_graph_port_is_available(node)) { - dev_dbg(fbi->dev, "register vpl for %s\n", dev->parent->device_node->full_name); + dev_dbg(fbi->dev, "register vpl for %pOF\n", + dev->parent->of_node); - fbi->vpl.node = dev->parent->device_node; + fbi->vpl.node = dev->parent->of_node; ret = vpl_register(&fbi->vpl); if (ret) return ret; @@ -336,6 +331,7 @@ static int ipufb_probe(struct device_d *dev) if (ret) dev_dbg(fbi->dev, "failed to get modes: %s\n", strerror(-ret)); + info->dev.parent = dev; ret = register_framebuffer(info); if (ret < 0) { dev_err(fbi->dev, "failed to register framebuffer\n"); @@ -346,18 +342,14 @@ static int ipufb_probe(struct device_d *dev) return ret; } -static void ipufb_remove(struct device_d *dev) +static void ipufb_remove(struct device *dev) { } -static struct driver_d ipufb_driver = { +static struct driver ipufb_driver = { .name = "imx-ipuv3-crtc", .probe = ipufb_probe, .remove = ipufb_remove, }; -static int ipufb_register(void) -{ - return platform_driver_register(&ipufb_driver); -} -late_initcall(ipufb_register); +late_platform_driver(ipufb_driver); diff --git a/drivers/video/imx-ipu-v3/ipuv3-plane.c b/drivers/video/imx-ipu-v3/ipuv3-plane.c index 9dffcfc670..aed7a46963 100644 --- a/drivers/video/imx-ipu-v3/ipuv3-plane.c +++ b/drivers/video/imx-ipu-v3/ipuv3-plane.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * i.MX IPUv3 DP Overlay Planes * * Copyright (C) 2013 Philipp Zabel, Pengutronix - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * 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 <common.h> #include <linux/err.h> @@ -63,7 +55,7 @@ int ipu_plane_mode_set(struct ipu_plane *ipu_plane, uint32_t src_w, uint32_t src_h) { struct ipu_ch_param __iomem *cpmem; - struct device_d *dev = &info->dev; + struct device *dev = &info->dev; int ret; /* no scaling */ diff --git a/drivers/video/imx-ipu-v3/ipuv3-plane.h b/drivers/video/imx-ipu-v3/ipuv3-plane.h index 3d52f807fe..cade98e3e9 100644 --- a/drivers/video/imx-ipu-v3/ipuv3-plane.h +++ b/drivers/video/imx-ipu-v3/ipuv3-plane.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef __IPUV3_PLANE_H__ #define __IPUV3_PLANE_H__ diff --git a/drivers/video/imx.c b/drivers/video/imx.c index d15c2d88fb..cb1c11b4cb 100644 --- a/drivers/video/imx.c +++ b/drivers/video/imx.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Freescale i.MX Frame Buffer device driver * * Copyright (C) 2004 Sascha Hauer, Pengutronix * Based on acornfb.c Copyright (C) Russell King. * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - * * Please direct your questions and comments on this driver to the following * email address: * @@ -17,7 +14,7 @@ #include <common.h> #include <fb.h> #include <io.h> -#include <mach/imxfb.h> +#include <platform_data/imxfb.h> #include <driver.h> #include <malloc.h> #include <errno.h> @@ -25,7 +22,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/sizes.h> -#include <asm-generic/div64.h> +#include <linux/math64.h> #define LCDC_SSA 0x00 @@ -153,7 +150,7 @@ struct imxfb_info { unused:30; struct fb_info info; - struct device_d *dev; + struct device *dev; void (*enable)(int enable); @@ -381,7 +378,7 @@ static struct fb_ops imxfb_ops = { .fb_activate_var = imxfb_activate_var, }; -static int imxfb_allocate_fbbuffer(const struct device_d *dev, +static int imxfb_allocate_fbbuffer(const struct device *dev, struct fb_info *info, void *forcefb) { size_t fbsize = info->xres * info->yres * (info->bits_per_pixel >> 3); @@ -404,7 +401,7 @@ static int imxfb_allocate_fbbuffer(const struct device_d *dev, * allocation as necessary, but in the absense of a better * function just use it. */ - info->screen_base = memalign(fbsize, SZ_4M); + info->screen_base = memalign(SZ_4M, fbsize); if (!info->screen_base) return -ENOMEM; memset(info->screen_base, 0, fbsize); @@ -514,6 +511,7 @@ static int imxfb_register_overlay(struct imxfb_info *fbi, void *fb) overlay->blue = rgb->blue; overlay->transp = rgb->transp; + overlay->dev.parent = &fbi->info.dev; ret = register_framebuffer(overlay); if (ret < 0) { dev_err(fbi->dev, "failed to register framebuffer\n"); @@ -527,7 +525,7 @@ static int imxfb_register_overlay(struct imxfb_info *fbi, void *fb) } #endif -static int imxfb_probe(struct device_d *dev) +static int imxfb_probe(struct device *dev) { struct resource *iores; struct imxfb_info *fbi; @@ -592,6 +590,7 @@ static int imxfb_probe(struct device_d *dev) imxfb_activate_var(&fbi->info); + fbi->info.dev.parent = dev; ret = register_framebuffer(&fbi->info); if (ret < 0) { dev_err(dev, "failed to register framebuffer\n"); @@ -603,7 +602,7 @@ static int imxfb_probe(struct device_d *dev) return 0; } -static struct driver_d imxfb_driver = { +static struct driver imxfb_driver = { .name = "imxfb", .probe = imxfb_probe, }; diff --git a/drivers/video/mipi_dbi.c b/drivers/video/mipi_dbi.c new file mode 100644 index 0000000000..2f8d6ecc72 --- /dev/null +++ b/drivers/video/mipi_dbi.c @@ -0,0 +1,745 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MIPI Display Bus Interface (DBI) LCD controller support + * + * Copyright 2016 Noralf Trønnes + */ + +#define pr_fmt(fmt) "mipi-dbi: " fmt + +#include <common.h> +#include <dma.h> +#include <linux/kernel.h> +#include <linux/sizes.h> +#include <linux/gpio/consumer.h> +#include <regulator.h> +#include <spi/spi.h> +#include <video/backlight.h> +#include <video/mipi_dbi.h> + +#include <video/vpl.h> +#include <video/mipi_display.h> +#include <video/fourcc.h> + +#define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */ + +#define DCS_POWER_MODE_DISPLAY BIT(2) +#define DCS_POWER_MODE_DISPLAY_NORMAL_MODE BIT(3) +#define DCS_POWER_MODE_SLEEP_MODE BIT(4) +#define DCS_POWER_MODE_PARTIAL_MODE BIT(5) +#define DCS_POWER_MODE_IDLE_MODE BIT(6) +#define DCS_POWER_MODE_RESERVED_MASK (BIT(0) | BIT(1) | BIT(7)) + +LIST_HEAD(mipi_dbi_list); +EXPORT_SYMBOL(mipi_dbi_list); + +/** + * DOC: overview + * + * This library provides helpers for MIPI Display Bus Interface (DBI) + * compatible display controllers. + * + * Many controllers for tiny lcd displays are MIPI compliant and can use this + * library. If a controller uses registers 0x2A and 0x2B to set the area to + * update and uses register 0x2C to write to frame memory, it is most likely + * MIPI compliant. + * + * Only MIPI Type 1 displays are supported since a full frame memory is needed. + * + * There are 3 MIPI DBI implementation types: + * + * A. Motorola 6800 type parallel bus + * + * B. Intel 8080 type parallel bus + * + * C. SPI type with 3 options: + * + * 1. 9-bit with the Data/Command signal as the ninth bit + * 2. Same as above except it's sent as 16 bits + * 3. 8-bit with the Data/Command signal as a separate D/CX pin + * + * Currently barebox mipi_dbi only supports Type C option 3 with + * mipi_dbi_spi_init(). + */ + +#define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \ +({ \ + if (!len) \ + pr_debug("cmd=%02x\n", cmd); \ + else if (len <= 32) \ + pr_debug("cmd=%02x, par=%*ph\n", cmd, (int)len, data);\ + else \ + pr_debug("cmd=%02x, len=%zu\n", cmd, len); \ +}) + +static const u8 mipi_dbi_dcs_read_commands[] = { + MIPI_DCS_GET_DISPLAY_ID, + MIPI_DCS_GET_RED_CHANNEL, + MIPI_DCS_GET_GREEN_CHANNEL, + MIPI_DCS_GET_BLUE_CHANNEL, + MIPI_DCS_GET_DISPLAY_STATUS, + MIPI_DCS_GET_POWER_MODE, + MIPI_DCS_GET_ADDRESS_MODE, + MIPI_DCS_GET_PIXEL_FORMAT, + MIPI_DCS_GET_DISPLAY_MODE, + MIPI_DCS_GET_SIGNAL_MODE, + MIPI_DCS_GET_DIAGNOSTIC_RESULT, + MIPI_DCS_READ_MEMORY_START, + MIPI_DCS_READ_MEMORY_CONTINUE, + MIPI_DCS_GET_SCANLINE, + MIPI_DCS_GET_DISPLAY_BRIGHTNESS, + MIPI_DCS_GET_CONTROL_DISPLAY, + MIPI_DCS_GET_POWER_SAVE, + MIPI_DCS_GET_CABC_MIN_BRIGHTNESS, + MIPI_DCS_READ_DDB_START, + MIPI_DCS_READ_DDB_CONTINUE, + 0, /* sentinel */ +}; + +bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd) +{ + unsigned int i; + + if (!dbi->read_commands) + return false; + + for (i = 0; i < 0xff; i++) { + if (!dbi->read_commands[i]) + return false; + if (cmd == dbi->read_commands[i]) + return true; + } + + return false; +} + +int mipi_dbi_command_read_len(int cmd) +{ + switch (cmd) { + case MIPI_DCS_READ_MEMORY_START: + case MIPI_DCS_READ_MEMORY_CONTINUE: + return 2; + case MIPI_DCS_GET_DISPLAY_ID: + return 3; + case MIPI_DCS_GET_DISPLAY_STATUS: + return 4; + default: + return 1; + } +} + +/** + * mipi_dbi_command_read - MIPI DCS read command + * @dbi: MIPI DBI structure + * @cmd: Command + * @val: Value read + * + * Send MIPI DCS read command to the controller. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val) +{ + if (!dbi->read_commands) + return -EACCES; + + if (!mipi_dbi_command_is_read(dbi, cmd)) + return -EINVAL; + + return mipi_dbi_command_buf(dbi, cmd, val, 1); +} +EXPORT_SYMBOL(mipi_dbi_command_read); + +/** + * mipi_dbi_command_buf - MIPI DCS command with parameter(s) in an array + * @dbi: MIPI DBI structure + * @cmd: Command + * @data: Parameter buffer + * @len: Buffer length + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len) +{ + u8 *cmdbuf; + int ret; + + /* SPI requires dma-safe buffers */ + cmdbuf = kmemdup(&cmd, 1, GFP_KERNEL); + if (!cmdbuf) + return -ENOMEM; + + ret = dbi->command(dbi, cmdbuf, data, len); + + kfree(cmdbuf); + + return ret; +} +EXPORT_SYMBOL(mipi_dbi_command_buf); + +/* This should only be used by mipi_dbi_command() */ +int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data, + size_t len) +{ + u8 *buf; + int ret; + + buf = kmemdup(data, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = mipi_dbi_command_buf(dbi, cmd, buf, len); + + kfree(buf); + + return ret; +} +EXPORT_SYMBOL(mipi_dbi_command_stackbuf); + +/** + * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary + * @dst: The destination buffer + * @info: The source framebuffer info + * @clip: Clipping rectangle of the area to be copied + * @swap: When true, swap MSB/LSB of 16-bit values + */ +static void mipi_dbi_buf_copy(u16 *dst, struct fb_info *info, + struct fb_rect *clip, bool swap) +{ + u16 *src = (u16 *)info->screen_base; + unsigned int height = clip->y2 - clip->y1; + unsigned int width = clip->x2 - clip->x1; + int x, y; + + src += clip->y1 * info->xres + clip->x1; + if (swap) { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + *dst++ = src[x] << 8 | src[x] >> 8; + src += info->xres; + } + } else { + for (y = 0; y < height; y++) { + memcpy(dst, src, 2 * width); + dst += width; + src += info->xres; + } + } +} + +static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev, + unsigned int xs, unsigned int xe, + unsigned int ys, unsigned int ye) +{ + struct mipi_dbi *dbi = &dbidev->dbi; + + xs += dbidev->mode.left_margin; + xe += dbidev->mode.left_margin; + ys += dbidev->mode.upper_margin; + ye += dbidev->mode.upper_margin; + + mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, (xs >> 8) & 0xff, + xs & 0xff, (xe >> 8) & 0xff, xe & 0xff); + mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, (ys >> 8) & 0xff, + ys & 0xff, (ye >> 8) & 0xff, ye & 0xff); +} + +static void mipi_dbi_fb_dirty(struct mipi_dbi_dev *dbidev, struct fb_info *info, + struct fb_rect *rect) +{ + unsigned int height = rect->y2 - rect->y1; + unsigned int width = rect->x2 - rect->x1; + struct mipi_dbi *dbi = &dbidev->dbi; + bool swap = dbi->swap_bytes; + int ret; + bool full; + void *tr; + + full = width == info->xres && height == info->yres; + + if (!full || swap) { + tr = dbidev->tx_buf; + mipi_dbi_buf_copy(tr, info, rect, swap); + } else { + tr = info->screen_base; + } + + mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1, + rect->y2 - 1); + + ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, tr, + width * height * 2); + if (ret) + pr_err_once("Failed to update display %d\n", ret); + + dbidev->damage.x1 = 0; + dbidev->damage.y1 = 0; + dbidev->damage.x2 = 0; + dbidev->damage.y2 = 0; +} + +/** + * mipi_dbi_enable_flush - MIPI DBI enable helper + * @dbidev: MIPI DBI device structure + * @info: Framebuffer info + * + * Flushes the whole framebuffer and enables the backlight. Drivers can use this + * in their &fb_ops->fb_enable callback. + */ +void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, + struct fb_info *info) +{ + struct fb_rect rect = { + .x1 = 0, + .y1 = 0, + .x2 = info->xres, + .y2 = info->yres + }; + + mipi_dbi_fb_dirty(dbidev, info, &rect); + + if (dbidev->backlight) + backlight_set_brightness_default(dbidev->backlight); +} +EXPORT_SYMBOL(mipi_dbi_enable_flush); + +static void mipi_dbi_blank(struct mipi_dbi_dev *dbidev) +{ + u16 height = dbidev->mode.xres; + u16 width = dbidev->mode.yres; + struct mipi_dbi *dbi = &dbidev->dbi; + size_t len = width * height * 2; + + memset(dbidev->tx_buf, 0, len); + + mipi_dbi_set_window_address(dbidev, 0, width - 1, 0, height - 1); + mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, dbidev->tx_buf, len); +} + +/** + * mipi_dbi_fb_disable - MIPI DBI framebuffer disable helper + * @info: Framebuffer info + * + * This function disables backlight if present, if not the display memory is + * blanked. The regulator is disabled if in use. Drivers can use this as their + * &fb_ops->fb_disable callback. + */ +void mipi_dbi_fb_disable(struct fb_info *info) +{ + struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info); + + if (dbidev->backlight) + backlight_set_brightness(dbidev->backlight, 0); + else + mipi_dbi_blank(dbidev); + + regulator_disable(dbidev->regulator); + regulator_disable(dbidev->io_regulator); +} +EXPORT_SYMBOL(mipi_dbi_fb_disable); + +void mipi_dbi_fb_damage(struct fb_info *info, const struct fb_rect *rect) +{ + struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info); + + if (dbidev->damage.x2 && dbidev->damage.y2) { + dbidev->damage.x1 = min(dbidev->damage.x1, rect->x1); + dbidev->damage.y1 = min(dbidev->damage.y1, rect->y1); + dbidev->damage.x2 = max(dbidev->damage.x2, rect->x2); + dbidev->damage.y2 = max(dbidev->damage.y2, rect->y2); + } else { + dbidev->damage = *rect; + } +} +EXPORT_SYMBOL(mipi_dbi_fb_damage); + +void mipi_dbi_fb_flush(struct fb_info *info) +{ + struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info); + + if (!dbidev->damage.x2 || !dbidev->damage.y2) { + dbidev->damage.x1 = 0; + dbidev->damage.y1 = 0; + dbidev->damage.x2 = info->xres; + dbidev->damage.y2 = info->yres; + } + + mipi_dbi_fb_dirty(dbidev, info, &dbidev->damage); +} +EXPORT_SYMBOL(mipi_dbi_fb_flush); + +/** + * mipi_dbi_dev_init - MIPI DBI device initialization + * @dbidev: MIPI DBI device structure to initialize + * @ops: Framebuffer operations + * @mode: Display mode + * + * This function sets up a &fb_info with one fixed &fb_videomode. + * Additionally &mipi_dbi.tx_buf is allocated. + * + * Supported format: RGB565. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, struct fb_ops *ops, + struct fb_videomode *mode) +{ + struct fb_info *info = &dbidev->info; + + info->mode = mode; + info->fbops = ops; + info->dev.parent = dbidev->dev; + + info->xres = mode->xres; + info->yres = mode->yres; + info->bits_per_pixel = 16; + info->line_length = info->xres * 2; + info->screen_size = info->line_length * info->yres; + info->screen_base = dma_alloc(info->screen_size); + memset(info->screen_base, 0, info->screen_size); + + info->red.length = 5; + info->red.offset = 11; + info->green.length = 6; + info->green.offset = 5; + info->blue.length = 5; + info->blue.offset = 0; + + dbidev->tx_buf = dma_alloc(info->screen_size); + + return 0; +} + +/** + * mipi_dbi_hw_reset - Hardware reset of controller + * @dbi: MIPI DBI structure + * + * Reset controller if the &mipi_dbi->reset gpio is set. + */ +void mipi_dbi_hw_reset(struct mipi_dbi *dbi) +{ + if (!dbi->reset) + return; + + gpiod_set_value(dbi->reset, 0); + udelay(20); + gpiod_set_value(dbi->reset, 1); + mdelay(120); +} +EXPORT_SYMBOL(mipi_dbi_hw_reset); + +/** + * mipi_dbi_display_is_on - Check if display is on + * @dbi: MIPI DBI structure + * + * This function checks the Power Mode register (if readable) to see if + * display output is turned on. This can be used to see if the bootloader + * has already turned on the display avoiding flicker when the pipeline is + * enabled. + * + * Returns: + * true if the display can be verified to be on, false otherwise. + */ +bool mipi_dbi_display_is_on(struct mipi_dbi *dbi) +{ + u8 val; + + if (mipi_dbi_command_read(dbi, MIPI_DCS_GET_POWER_MODE, &val)) + return false; + + val &= ~DCS_POWER_MODE_RESERVED_MASK; + + /* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */ + if (val != (DCS_POWER_MODE_DISPLAY | + DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE)) + return false; + + pr_debug("Display is ON\n"); + + return true; +} +EXPORT_SYMBOL(mipi_dbi_display_is_on); + +static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool cond) +{ + struct device *dev = dbidev->dev; + struct mipi_dbi *dbi = &dbidev->dbi; + int ret; + + ret = regulator_enable(dbidev->regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator (%d)\n", ret); + return ret; + } + + ret = regulator_enable(dbidev->io_regulator); + if (ret) { + dev_err(dev, "Failed to enable I/O regulator (%d)\n", ret); + regulator_disable(dbidev->regulator); + return ret; + } + + if (cond && mipi_dbi_display_is_on(dbi)) + return 1; + + mipi_dbi_hw_reset(dbi); + ret = mipi_dbi_command(dbi, MIPI_DCS_SOFT_RESET); + if (ret) { + dev_err(dev, "Failed to send reset command (%d)\n", ret); + regulator_disable(dbidev->io_regulator); + regulator_disable(dbidev->regulator); + return ret; + } + + /* + * If we did a hw reset, we know the controller is in Sleep mode and + * per MIPI DSC spec should wait 5ms after soft reset. If we didn't, + * we assume worst case and wait 120ms. + */ + if (dbi->reset) + mdelay(5); + else + mdelay(120); + + return 0; +} + +/** + * mipi_dbi_poweron_conditional_reset - MIPI DBI poweron and conditional reset + * @dbidev: MIPI DBI device structure + * + * This function enables the regulator if used and if the display is off, it + * does a hardware and software reset. If mipi_dbi_display_is_on() determines + * that the display is on, no reset is performed. + * + * Returns: + * Zero if the controller was reset, 1 if the display was already on, or a + * negative error code. + */ +int mipi_dbi_poweron_conditional_reset(struct mipi_dbi_dev *dbidev) +{ + return mipi_dbi_poweron_reset_conditional(dbidev, true); +} +EXPORT_SYMBOL(mipi_dbi_poweron_conditional_reset); + +#if IS_ENABLED(CONFIG_SPI) + +/** + * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed + * @spi: SPI device + * @len: The transfer buffer length. + * + * Many controllers have a max speed of 10MHz, but can be pushed way beyond + * that. Increase reliability by running pixel data at max speed and the rest + * at 10MHz, preventing transfer glitches from messing up the init settings. + */ +u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len) +{ + if (len > 64) + return 0; /* use default */ + + return min_t(u32, 10000000, spi->max_speed_hz); +} +EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed); + +static bool mipi_dbi_machine_little_endian(void) +{ +#if defined(__LITTLE_ENDIAN) + return true; +#else + return false; +#endif +} + +/* MIPI DBI Type C Option 3 */ + +static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd, + u8 *data, size_t len) +{ + struct spi_device *spi = dbi->spi; + u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED, + spi->max_speed_hz / 2); + struct spi_transfer tr[2] = { + { + .speed_hz = speed_hz, + .tx_buf = cmd, + .len = 1, + }, { + .speed_hz = speed_hz, + .len = len, + }, + }; + struct spi_message m; + u8 *buf; + int ret; + + if (!len) + return -EINVAL; + + /* + * Support non-standard 24-bit and 32-bit Nokia read commands which + * start with a dummy clock, so we need to read an extra byte. + */ + if (*cmd == MIPI_DCS_GET_DISPLAY_ID || + *cmd == MIPI_DCS_GET_DISPLAY_STATUS) { + if (!(len == 3 || len == 4)) + return -EINVAL; + + tr[1].len = len + 1; + } + + buf = kmalloc(tr[1].len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + tr[1].rx_buf = buf; + gpiod_set_value(dbi->dc, 0); + + spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr)); + ret = spi_sync(spi, &m); + if (ret) + goto err_free; + + if (tr[1].len == len) { + memcpy(data, buf, len); + } else { + unsigned int i; + + for (i = 0; i < len; i++) + data[i] = (buf[i] << 1) | (buf[i + 1] >> 7); + } + + MIPI_DBI_DEBUG_COMMAND(*cmd, data, len); + +err_free: + kfree(buf); + + return ret; +} + +static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd, + u8 *par, size_t num) +{ + struct spi_device *spi = dbi->spi; + unsigned int bpw = 8; + u32 speed_hz; + int ret; + + if (mipi_dbi_command_is_read(dbi, *cmd)) + return mipi_dbi_typec3_command_read(dbi, cmd, par, num); + + MIPI_DBI_DEBUG_COMMAND(*cmd, par, num); + + gpiod_set_value(dbi->dc, 0); + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); + ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1); + if (ret || !num) + return ret; + + if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !dbi->swap_bytes) + bpw = 16; + + gpiod_set_value(dbi->dc, 1); + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); + + return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); +} + +/** + * mipi_dbi_spi_init - Initialize MIPI DBI SPI interface + * @spi: SPI device + * @dbi: MIPI DBI structure to initialize + * @dc: D/C gpio + * + * This function sets &mipi_dbi->command, enables &mipi_dbi->read_commands for the + * usual read commands. It should be followed by a call to mipi_dbi_dev_init() or + * a driver-specific init. + * + * Type C Option 3 interface is assumed, Type C Option 1 is not yet supported, + * because barebox has no generic way yet to require a 9-bit SPI transfer + * + * If the SPI master driver doesn't support the necessary bits per word, + * the following transformation is used: + * + * - 9-bit: reorder buffer as 9x 8-bit words, padded with no-op command. + * - 16-bit: if big endian send as 8-bit, if little endian swap bytes + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi, + struct gpio_desc *dc) +{ + struct device *dev = &spi->dev; + + dbi->spi = spi; + dbi->read_commands = mipi_dbi_dcs_read_commands; + + if (!dc) { + dev_dbg(dev, "MIPI DBI Type-C 1 unsupported\n"); + return -ENOSYS; + } + + dbi->command = mipi_dbi_typec3_command; + dbi->dc = dc; + if (mipi_dbi_machine_little_endian() && !spi_is_bpw_supported(spi, 16)) + dbi->swap_bytes = true; + + dev_dbg(dev, "SPI speed: %uMHz\n", spi->max_speed_hz / 1000000); + + list_add(&dbi->list, &mipi_dbi_list); + return 0; +} +EXPORT_SYMBOL(mipi_dbi_spi_init); + +/** + * mipi_dbi_spi_transfer - SPI transfer helper + * @spi: SPI device + * @speed_hz: Override speed (optional) + * @bpw: Bits per word + * @buf: Buffer to transfer + * @len: Buffer length + * + * This SPI transfer helper breaks up the transfer of @buf into chunks which + * the SPI controller driver can handle. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz, + u8 bpw, const void *buf, size_t len) +{ + size_t max_chunk = spi_max_transfer_size(spi); + struct spi_transfer tr = { + .bits_per_word = bpw, + .speed_hz = speed_hz, + }; + struct spi_message m; + size_t chunk; + int ret; + + spi_message_init_with_transfers(&m, &tr, 1); + + while (len) { + chunk = min(len, max_chunk); + + tr.tx_buf = buf; + tr.len = chunk; + buf += chunk; + len -= chunk; + + ret = spi_sync(spi, &m); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(mipi_dbi_spi_transfer); + +#endif /* CONFIG_SPI */ + +MODULE_LICENSE("GPL"); diff --git a/drivers/video/mtl017.c b/drivers/video/mtl017.c index c04875cd07..ba214b47ae 100644 --- a/drivers/video/mtl017.c +++ b/drivers/video/mtl017.c @@ -1,19 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (C) Copyright 2014 Sascha Hauer, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <common.h> #include <driver.h> @@ -29,7 +16,7 @@ struct mtl017 { struct vpl vpl; - struct device_d *dev; + struct device *dev; struct i2c_client *client; u8 *regs; int enable_gpio; @@ -231,14 +218,14 @@ forward: return vpl_ioctl(&mtl017->vpl, 1, cmd, ptr); } -static int mtl017_probe(struct device_d *dev) +static int mtl017_probe(struct device *dev) { struct mtl017 *mtl017; int ret; enum of_gpio_flags flags; mtl017 = xzalloc(sizeof(struct mtl017)); - mtl017->vpl.node = dev->device_node; + mtl017->vpl.node = dev->of_node; mtl017->vpl.ioctl = mtl017_ioctl; mtl017->dev = dev; mtl017->client = to_i2c_client(dev); @@ -247,15 +234,16 @@ static int mtl017_probe(struct device_d *dev) if (IS_ERR(mtl017->regulator)) mtl017->regulator = NULL; - mtl017->enable_gpio = of_get_named_gpio_flags(dev->device_node, - "enable-gpios", 0, &flags); + mtl017->enable_gpio = of_get_named_gpio_flags(dev->of_node, + "enable-gpios", 0, + &flags); if (gpio_is_valid(mtl017->enable_gpio)) { if (!(flags & OF_GPIO_ACTIVE_LOW)) mtl017->enable_active_high = 1; } - mtl017->reset_gpio = of_get_named_gpio_flags(dev->device_node, - "reset-gpios", 0, &flags); + mtl017->reset_gpio = of_get_named_gpio_flags(dev->of_node, + "reset-gpios", 0, &flags); if (gpio_is_valid(mtl017->reset_gpio)) { if (!(flags & OF_GPIO_ACTIVE_LOW)) mtl017->reset_active_high = 1; @@ -268,7 +256,7 @@ static int mtl017_probe(struct device_d *dev) return 0; } -static struct driver_d mtl_driver = { +static struct driver mtl_driver = { .name = "mtl017", .probe = mtl017_probe, }; diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c index 17d8823c46..74b01239cb 100644 --- a/drivers/video/of_display_timing.c +++ b/drivers/video/of_display_timing.c @@ -1,11 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OF helpers for parsing display timings * * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix * * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de> - * - * This file is released under the GPLv2 */ #include <common.h> #include <of.h> @@ -38,8 +37,8 @@ static int parse_timing_property(const struct device_node *np, const char *name, prop = of_find_property(np, name, &length); if (!prop) { - pr_err("%s: could not find property %s\n", - np->full_name, name); + pr_err("%pOF: could not find property %s\n", + np, name); return -EINVAL; } @@ -47,8 +46,8 @@ static int parse_timing_property(const struct device_node *np, const char *name, if ((cells == 1) || (cells == 3)) { ret = of_property_read_u32(np, name, res); } else { - pr_err("%s: illegal timing specification in %s\n", - np->full_name, name); + pr_err("%pOF: illegal timing specification in %s\n", + np, name); return -EINVAL; } @@ -91,8 +90,7 @@ static int of_parse_display_timing(const struct device_node *np, DISPLAY_FLAGS_PIXDATA_NEGEDGE; if (ret) { - pr_err("%s: error reading timing properties\n", - np->full_name); + pr_err("%pOF: error reading timing properties\n", np); return -EINVAL; } @@ -100,6 +98,28 @@ static int of_parse_display_timing(const struct device_node *np, } /** + * of_get_display_timing - parse a display_timing entry + * @np: device_node with the timing subnode + * @name: name of the timing node + * @mode: fb_videomode struct to fill + **/ +int of_get_display_timing(const struct device_node *np, const char *name, + struct fb_videomode *mode) +{ + struct device_node *timing_np; + + if (!np) + return -EINVAL; + + timing_np = of_get_child_by_name(np, name); + if (!timing_np) + return -ENOENT; + + return of_parse_display_timing(timing_np, mode); +} +EXPORT_SYMBOL_GPL(of_get_display_timing); + +/** * of_get_display_timings - parse all display_timing entries from a device_node * @np: device_node with the subnodes **/ @@ -115,8 +135,7 @@ struct display_timings *of_get_display_timings(struct device_node *np) timings_np = of_get_child_by_name(np, "display-timings"); if (!timings_np) { - pr_debug("%s: could not find display-timings node\n", - np->full_name); + pr_debug("%pOF: could not find display-timings node\n", np); return NULL; } @@ -128,20 +147,19 @@ struct display_timings *of_get_display_timings(struct device_node *np) entry = of_get_next_available_child(np, NULL); /* if there is no child, it is useless to go on */ if (!entry) { - pr_err("%s: no timing specifications given\n", - np->full_name); + pr_err("%pOF: no timing specifications given\n", np); goto fail; } - pr_debug("%s: using %s as default timing\n", - np->full_name, entry->name); + pr_debug("%pOF: using %s as default timing\n", + np, entry->name); native_mode = entry; disp->num_modes = of_get_child_count(timings_np); if (disp->num_modes == 0) { /* should never happen, as entry was already found above */ - pr_err("%s: no timings specified\n", np->full_name); + pr_err("%pOF: no timings specified\n", np); goto fail; } @@ -162,8 +180,8 @@ struct display_timings *of_get_display_timings(struct device_node *np) * to not encourage wrong devicetrees, fail in case of * an error */ - pr_err("%s: error in timing %d\n", - np->full_name, disp->num_modes + 1); + pr_err("%pOF: error in timing %d\n", + np, disp->num_modes + 1); goto fail; } @@ -175,8 +193,8 @@ struct display_timings *of_get_display_timings(struct device_node *np) disp->num_modes++; } - pr_debug("%s: got %d timings. Using timing #%d as default\n", - np->full_name, disp->num_modes, + pr_debug("%pOF: got %d timings. Using timing #%d as default\n", + np, disp->num_modes, disp->native_mode + 1); return disp; diff --git a/drivers/video/omap.c b/drivers/video/omap.c index 884365f609..3b1ec89c38 100644 --- a/drivers/video/omap.c +++ b/drivers/video/omap.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * TI Omap Frame Buffer device driver * * Copyright (C) 2013 Christoph Fritz <chf.fritz@googlemail.com> * Based on work by Enrico Scholz, sponsored by Phytec - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <driver.h> @@ -28,12 +16,10 @@ #include <io.h> #include <common.h> #include <malloc.h> -#include <common.h> #include <clock.h> #include <linux/err.h> -#include <mach/omap4-silicon.h> -#include <mach/omap-fb.h> +#include <video/omap-fb.h> #include <mmu.h> @@ -41,7 +27,7 @@ struct omapfb_device { struct fb_info info; - struct device_d *dev; + struct device *dev; struct omapfb_display const *cur_display; @@ -424,7 +410,7 @@ static struct fb_ops omapfb_ops = { .fb_activate_var = omapfb_activate_var, }; -static int omapfb_probe(struct device_d *dev) +static int omapfb_probe(struct device *dev) { struct omapfb_platform_data const *pdata = dev->platform_data; struct omapfb_device *fbi; @@ -496,6 +482,7 @@ static int omapfb_probe(struct device_d *dev) goto out; } + info->dev.parent = dev; rc = register_framebuffer(info); if (rc < 0) { dev_err(dev, "failed to register framebuffer: %d\n", rc); @@ -512,14 +499,9 @@ out: return rc; } -static struct driver_d omapfb_driver = { +static struct driver omapfb_driver = { .name = "omap_fb", .probe = omapfb_probe, }; -static int omapfb_init(void) -{ - return platform_driver_register(&omapfb_driver); -} - -device_initcall(omapfb_init); +device_platform_driver(omapfb_driver); diff --git a/drivers/video/omap.h b/drivers/video/omap.h index ac9e1cee87..991d7a0f91 100644 --- a/drivers/video/omap.h +++ b/drivers/video/omap.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * TI Omap4 Frame Buffer device driver * * Copyright (C) 2013 Christoph Fritz <chf.fritz@googlemail.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 H_BAREBOX_DRIVER_VIDEO_OMAP4_REGS_H diff --git a/drivers/video/panel-ilitek-ili9341.c b/drivers/video/panel-ilitek-ili9341.c new file mode 100644 index 0000000000..4d03a8513e --- /dev/null +++ b/drivers/video/panel-ilitek-ili9341.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Ilitek ILI9341 TFT LCD driver. + * + * This panel can be configured to support: + * - 16-bit parallel RGB interface + * - 18-bit parallel RGB interface + * - 4-line serial spi interface + * + * Copyright 2018 David Lechner <david@lechnology.com> + * Copyright (C) 2021 Dillon Min <dillon.minfei@gmail.com> + * + * Derived from Linux drivers/drm/gpu/panel/panel-ilitek-ili9341.c + */ + +#include <common.h> +#include <linux/bitops.h> +#include <linux/gpio/consumer.h> +#include <of.h> +#include <regulator.h> +#include <spi/spi.h> +#include <video/vpl.h> +#include <video/mipi_dbi.h> + +#include <video/mipi_display.h> + +#define ILI9341_RGB_INTERFACE 0xb0 /* RGB Interface Signal Control */ +#define ILI9341_FRC 0xb1 /* Frame Rate Control register */ +#define ILI9341_DFC 0xb6 /* Display Function Control register */ +#define ILI9341_POWER1 0xc0 /* Power Control 1 register */ +#define ILI9341_POWER2 0xc1 /* Power Control 2 register */ +#define ILI9341_VCOM1 0xc5 /* VCOM Control 1 register */ +#define ILI9341_VCOM2 0xc7 /* VCOM Control 2 register */ +#define ILI9341_POWERA 0xcb /* Power control A register */ +#define ILI9341_POWERB 0xcf /* Power control B register */ +#define ILI9341_PGAMMA 0xe0 /* Positive Gamma Correction register */ +#define ILI9341_NGAMMA 0xe1 /* Negative Gamma Correction register */ +#define ILI9341_DTCA 0xe8 /* Driver timing control A */ +#define ILI9341_DTCB 0xea /* Driver timing control B */ +#define ILI9341_POWER_SEQ 0xed /* Power on sequence register */ +#define ILI9341_3GAMMA_EN 0xf2 /* 3 Gamma enable register */ +#define ILI9341_INTERFACE 0xf6 /* Interface control register */ +#define ILI9341_PRC 0xf7 /* Pump ratio control register */ +#define ILI9341_ETMOD 0xb7 /* Entry mode set */ + +#define ILI9341_MADCTL_BGR BIT(3) +#define ILI9341_MADCTL_MV BIT(5) +#define ILI9341_MADCTL_MX BIT(6) +#define ILI9341_MADCTL_MY BIT(7) + +#define ILI9341_POWER_B_LEN 3 +#define ILI9341_POWER_SEQ_LEN 4 +#define ILI9341_DTCA_LEN 3 +#define ILI9341_DTCB_LEN 2 +#define ILI9341_POWER_A_LEN 5 +#define ILI9341_DFC_1_LEN 2 +#define ILI9341_FRC_LEN 2 +#define ILI9341_VCOM_1_LEN 2 +#define ILI9341_DFC_2_LEN 4 +#define ILI9341_COLUMN_ADDR_LEN 4 +#define ILI9341_PAGE_ADDR_LEN 4 +#define ILI9341_INTERFACE_LEN 3 +#define ILI9341_PGAMMA_LEN 15 +#define ILI9341_NGAMMA_LEN 15 +#define ILI9341_CA_LEN 3 + +#define ILI9341_PIXEL_DPI_16_BITS (BIT(6) | BIT(4)) +#define ILI9341_PIXEL_DPI_18_BITS (BIT(6) | BIT(5)) +#define ILI9341_GAMMA_CURVE_1 BIT(0) +#define ILI9341_IF_WE_MODE BIT(0) +#define ILI9341_IF_BIG_ENDIAN 0x00 +#define ILI9341_IF_DM_RGB BIT(2) +#define ILI9341_IF_DM_INTERNAL 0x00 +#define ILI9341_IF_DM_VSYNC BIT(3) +#define ILI9341_IF_RM_RGB BIT(1) +#define ILI9341_IF_RIM_RGB 0x00 + +#define ILI9341_COLUMN_ADDR 0x00ef +#define ILI9341_PAGE_ADDR 0x013f + +#define ILI9341_RGB_EPL BIT(0) +#define ILI9341_RGB_DPL BIT(1) +#define ILI9341_RGB_HSPL BIT(2) +#define ILI9341_RGB_VSPL BIT(3) +#define ILI9341_RGB_DE_MODE BIT(6) +#define ILI9341_RGB_DISP_PATH_MEM BIT(7) + +#define ILI9341_DBI_VCOMH_4P6V 0x23 +#define ILI9341_DBI_PWR_2_DEFAULT 0x10 +#define ILI9341_DBI_PRC_NORMAL 0x20 +#define ILI9341_DBI_VCOM_1_VMH_4P25V 0x3e +#define ILI9341_DBI_VCOM_1_VML_1P5V 0x28 +#define ILI9341_DBI_VCOM_2_DEC_58 0x86 +#define ILI9341_DBI_FRC_DIVA 0x00 +#define ILI9341_DBI_FRC_RTNA 0x1b +#define ILI9341_DBI_EMS_GAS BIT(0) +#define ILI9341_DBI_EMS_DTS BIT(1) +#define ILI9341_DBI_EMS_GON BIT(2) + +/* struct ili9341_config - the system specific ILI9341 configuration */ +struct ili9341_config { + u32 max_spi_speed; + /* mode: the display mode */ + const struct fb_videomode mode; + /* ca: TODO: need comments for this register */ + u8 ca[ILI9341_CA_LEN]; + /* power_b: TODO: need comments for this register */ + u8 power_b[ILI9341_POWER_B_LEN]; + /* power_seq: TODO: need comments for this register */ + u8 power_seq[ILI9341_POWER_SEQ_LEN]; + /* dtca: TODO: need comments for this register */ + u8 dtca[ILI9341_DTCA_LEN]; + /* dtcb: TODO: need comments for this register */ + u8 dtcb[ILI9341_DTCB_LEN]; + /* power_a: TODO: need comments for this register */ + u8 power_a[ILI9341_POWER_A_LEN]; + /* frc: Frame Rate Control (In Normal Mode/Full Colors) (B1h) */ + u8 frc[ILI9341_FRC_LEN]; + /* prc: TODO: need comments for this register */ + u8 prc; + /* dfc_1: B6h DISCTRL (Display Function Control) */ + u8 dfc_1[ILI9341_DFC_1_LEN]; + /* power_1: Power Control 1 (C0h) */ + u8 power_1; + /* power_2: Power Control 2 (C1h) */ + u8 power_2; + /* vcom_1: VCOM Control 1(C5h) */ + u8 vcom_1[ILI9341_VCOM_1_LEN]; + /* vcom_2: VCOM Control 2(C7h) */ + u8 vcom_2; + /* address_mode: Memory Access Control (36h) */ + u8 address_mode; + /* g3amma_en: TODO: need comments for this register */ + u8 g3amma_en; + /* rgb_interface: RGB Interface Signal Control (B0h) */ + u8 rgb_interface; + /* dfc_2: refer to dfc_1 */ + u8 dfc_2[ILI9341_DFC_2_LEN]; + /* column_addr: Column Address Set (2Ah) */ + u8 column_addr[ILI9341_COLUMN_ADDR_LEN]; + /* page_addr: Page Address Set (2Bh) */ + u8 page_addr[ILI9341_PAGE_ADDR_LEN]; + /* interface: Interface Control (F6h) */ + u8 interface[ILI9341_INTERFACE_LEN]; + /* + * pixel_format: This command sets the pixel format for the RGB + * image data used by + */ + u8 pixel_format; + /* + * gamma_curve: This command is used to select the desired Gamma + * curve for the + */ + u8 gamma_curve; + /* pgamma: Positive Gamma Correction (E0h) */ + u8 pgamma[ILI9341_PGAMMA_LEN]; + /* ngamma: Negative Gamma Correction (E1h) */ + u8 ngamma[ILI9341_NGAMMA_LEN]; +}; + +struct ili9341 { + struct device *dev; + struct vpl vpl; + const struct ili9341_config *conf; + struct gpio_desc *reset_gpio; + struct gpio_desc *dc_gpio; + struct mipi_dbi *dbi; + u32 max_spi_speed; + struct regulator_bulk_data supplies[3]; +}; + +/* + * The Stm32f429-disco board has a panel ili9341 connected to ltdc controller + */ +static const struct ili9341_config ili9341_stm32f429_disco_data = { + .max_spi_speed = 10000000, + .mode = { + .name = "240x320", + .xres = 240, + .yres = 320, + .pixclock = KHZ2PICOS(6100), + .left_margin = 10, + .hsync_len = 10, + .right_margin = 20, + .upper_margin = 4, + .lower_margin = 2, + .vsync_len = 2, + }, + .ca = {0xc3, 0x08, 0x50}, + .power_b = {0x00, 0xc1, 0x30}, + .power_seq = {0x64, 0x03, 0x12, 0x81}, + .dtca = {0x85, 0x00, 0x78}, + .power_a = {0x39, 0x2c, 0x00, 0x34, 0x02}, + .prc = 0x20, + .dtcb = {0x00, 0x00}, + /* 0x00 fosc, 0x1b 70hz */ + .frc = {0x00, 0x1b}, + /* + * 0x0a Interval scan, AGND AGND AGND AGND + * 0xa2 Normally white, G1 -> G320, S720 -> S1, + * Scan Cycle 5 frames,85ms + */ + .dfc_1 = {0x0a, 0xa2}, + /* 0x10 3.65v */ + .power_1 = 0x10, + /* 0x10 AVDD=vci*2, VGH=vci*7, VGL=-vci*4 */ + .power_2 = 0x10, + /* 0x45 VCOMH 4.425v, 0x15 VCOML -1.975*/ + .vcom_1 = {0x45, 0x15}, + /* 0x90 offset voltage, VMH-48, VML-48 */ + .vcom_2 = 0x90, + /* + * 0xc8 Row Address Order, Column Address Order + * BGR 1 + */ + .address_mode = 0xc8, + .g3amma_en = 0x00, + /* + * 0xc2 + * Display Data Path: Memory + * RGB: DE mode + * DOTCLK polarity set (data fetched at the falling time) + */ + .rgb_interface = ILI9341_RGB_DISP_PATH_MEM | + ILI9341_RGB_DE_MODE | + ILI9341_RGB_DPL, + /* + * 0x0a + * Gate outputs in non-display area: Interval scan + * Determine source/VCOM output in a non-display area in the partial + * display mode: AGND AGND AGND AGND + * + * 0xa7 + * Scan Cycle: 15 frames + * fFLM = 60Hz: 255ms + * Liquid crystal type: Normally white + * Gate Output Scan Direction: G1 -> G320 + * Source Output Scan Direction: S720 -> S1 + * + * 0x27 + * LCD Driver Line: 320 lines + * + * 0x04 + * PCDIV: 4 + */ + .dfc_2 = {0x0a, 0xa7, 0x27, 0x04}, + /* column address: 240 */ + .column_addr = {0x00, 0x00, (ILI9341_COLUMN_ADDR >> 4) & 0xff, + ILI9341_COLUMN_ADDR & 0xff}, + /* page address: 320 */ + .page_addr = {0x00, 0x00, (ILI9341_PAGE_ADDR >> 4) & 0xff, + ILI9341_PAGE_ADDR & 0xff}, + /* + * Memory write control: When the transfer number of data exceeds + * (EC-SC+1)*(EP-SP+1), the column and page number will be + * reset, and the exceeding data will be written into the following + * column and page. + * Display Operation Mode: RGB Interface Mode + * Interface for RAM Access: RGB interface + * 16- bit RGB interface (1 transfer/pixel) + */ + .interface = {ILI9341_IF_WE_MODE, 0x00, + ILI9341_IF_DM_RGB | ILI9341_IF_RM_RGB}, + /* DPI: 16 bits / pixel */ + .pixel_format = ILI9341_PIXEL_DPI_16_BITS, + /* Curve Selected: Gamma curve 1 (G2.2) */ + .gamma_curve = ILI9341_GAMMA_CURVE_1, + .pgamma = {0x0f, 0x29, 0x24, 0x0c, 0x0e, + 0x09, 0x4e, 0x78, 0x3c, 0x09, + 0x13, 0x05, 0x17, 0x11, 0x00}, + .ngamma = {0x00, 0x16, 0x1b, 0x04, 0x11, + 0x07, 0x31, 0x33, 0x42, 0x05, + 0x0c, 0x0a, 0x28, 0x2f, 0x0f}, +}; + +static inline struct ili9341 *vpl_to_ili9341(struct vpl *vpl) +{ + return container_of(vpl, struct ili9341, vpl); +} + +static void ili9341_dpi_init(struct ili9341 *ili) +{ + struct device *dev = ili->dev; + struct mipi_dbi *dbi = ili->dbi; + struct ili9341_config *cfg = (struct ili9341_config *)ili->conf; + + /* Power Control */ + mipi_dbi_command_stackbuf(dbi, 0xca, cfg->ca, ILI9341_CA_LEN); + mipi_dbi_command_stackbuf(dbi, ILI9341_POWERB, cfg->power_b, + ILI9341_POWER_B_LEN); + mipi_dbi_command_stackbuf(dbi, ILI9341_POWER_SEQ, cfg->power_seq, + ILI9341_POWER_SEQ_LEN); + mipi_dbi_command_stackbuf(dbi, ILI9341_DTCA, cfg->dtca, + ILI9341_DTCA_LEN); + mipi_dbi_command_stackbuf(dbi, ILI9341_POWERA, cfg->power_a, + ILI9341_POWER_A_LEN); + mipi_dbi_command(ili->dbi, ILI9341_PRC, cfg->prc); + mipi_dbi_command_stackbuf(dbi, ILI9341_DTCB, cfg->dtcb, + ILI9341_DTCB_LEN); + mipi_dbi_command_stackbuf(dbi, ILI9341_FRC, cfg->frc, ILI9341_FRC_LEN); + mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_1, + ILI9341_DFC_1_LEN); + mipi_dbi_command(dbi, ILI9341_POWER1, cfg->power_1); + mipi_dbi_command(dbi, ILI9341_POWER2, cfg->power_2); + + /* VCOM */ + mipi_dbi_command_stackbuf(dbi, ILI9341_VCOM1, cfg->vcom_1, + ILI9341_VCOM_1_LEN); + mipi_dbi_command(dbi, ILI9341_VCOM2, cfg->vcom_2); + mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, cfg->address_mode); + + /* Gamma */ + mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, cfg->g3amma_en); + mipi_dbi_command(dbi, ILI9341_RGB_INTERFACE, cfg->rgb_interface); + mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_2, + ILI9341_DFC_2_LEN); + + /* Colomn address set */ + mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, + cfg->column_addr, ILI9341_COLUMN_ADDR_LEN); + + /* Page address set */ + mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_PAGE_ADDRESS, + cfg->page_addr, ILI9341_PAGE_ADDR_LEN); + mipi_dbi_command_stackbuf(dbi, ILI9341_INTERFACE, cfg->interface, + ILI9341_INTERFACE_LEN); + + /* Format */ + mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, cfg->pixel_format); + mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START); + mdelay(200); + mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, cfg->gamma_curve); + mipi_dbi_command_stackbuf(dbi, ILI9341_PGAMMA, cfg->pgamma, + ILI9341_PGAMMA_LEN); + mipi_dbi_command_stackbuf(dbi, ILI9341_NGAMMA, cfg->ngamma, + ILI9341_NGAMMA_LEN); + mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); + mdelay(200); + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); + mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START); + + dev_info(dev, "Initialized display rgb interface\n"); +} + +static int ili9341_dpi_power_on(struct ili9341 *ili) +{ + struct device *dev = ili->dev; + int ret = 0; + + /* Assert RESET */ + gpiod_set_value(ili->reset_gpio, 1); + + /* Enable power */ + ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies), + ili->supplies); + if (ret < 0) { + dev_err(dev, "unable to enable vcc\n"); + return ret; + } + mdelay(20); + + /* De-assert RESET */ + gpiod_set_value(ili->reset_gpio, 0); + mdelay(20); + + return 0; +} + +static int ili9341_dpi_power_off(struct ili9341 *ili) +{ + /* Assert RESET */ + gpiod_set_value(ili->reset_gpio, 1); + + /* Disable power */ + return regulator_bulk_disable(ARRAY_SIZE(ili->supplies), + ili->supplies); +} + +static void ili9341_dpi_disable(struct ili9341 *ili) +{ + mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_OFF); +} + +static int ili9341_dpi_prepare(struct ili9341 *ili) +{ + int ret; + + ret = ili9341_dpi_power_on(ili); + if (ret < 0) + return ret; + + ili9341_dpi_init(ili); + + return ret; +} + +static void ili9341_dpi_enable(struct ili9341 *ili) +{ + mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_ON); +} + +static int ili9341_dpi_get_modes(struct ili9341 *ili, + struct display_timings *timings) +{ + struct fb_videomode *mode; + + mode = memdup(&ili->conf->mode, sizeof(*mode)); + if (!mode) + return -ENOMEM; + + /* + * These are from the PoV of the display controller, so + * DPL=1 => display samples at positive edge + * => controller drives at negative edge + */ + if (ili->conf->rgb_interface & ILI9341_RGB_DPL) + mode->display_flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; + else + mode->display_flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; + + if (ili->conf->rgb_interface & ILI9341_RGB_EPL) + mode->display_flags |= DISPLAY_FLAGS_DE_HIGH; + else + mode->display_flags |= DISPLAY_FLAGS_DE_LOW; + + /* Set up the polarity */ + if (ili->conf->rgb_interface & ILI9341_RGB_HSPL) + mode->sync |= FB_SYNC_HOR_HIGH_ACT; + + if (ili->conf->rgb_interface & ILI9341_RGB_VSPL) + mode->sync |= FB_SYNC_VERT_HIGH_ACT; + + timings->modes = mode; + timings->num_modes = 1; + return 0; +} + +static int ili9341_ioctl(struct vpl *vpl, unsigned int port, + unsigned int cmd, void *ptr) +{ + struct ili9341 *ili = vpl_to_ili9341(vpl); + + switch (cmd) { + case VPL_PREPARE: + return ili9341_dpi_prepare(ili); + case VPL_ENABLE: + ili9341_dpi_enable(ili); + return 0; + case VPL_DISABLE: + ili9341_dpi_disable(ili); + return 0; + case VPL_UNPREPARE: + return ili9341_dpi_power_off(ili); + case VPL_GET_VIDEOMODES: + return ili9341_dpi_get_modes(ili, ptr); + default: + return 0; + } +} + +static int ili9341_dpi_probe(struct spi_device *spi, + struct gpio_desc *dc, struct gpio_desc *reset) +{ + struct device *dev = &spi->dev; + struct ili9341 *ili; + int ret; + + ili = kzalloc(sizeof(struct ili9341), GFP_KERNEL); + if (!ili) + return -ENOMEM; + + ili->dbi = kzalloc(sizeof(struct mipi_dbi), GFP_KERNEL); + if (!ili->dbi) + return -ENOMEM; + + ili->supplies[0].supply = "vci"; + ili->supplies[1].supply = "vddi"; + ili->supplies[2].supply = "vddi-led"; + ret = regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies), + ili->supplies); + if (ret < 0) { + dev_err(dev, "failed to get regulators: %d\n", ret); + return ret; + } + + ret = mipi_dbi_spi_init(spi, ili->dbi, dc); + if (ret) + return ret; + + ili->reset_gpio = reset; + /* + * Every new incarnation of this display must have a unique + * data entry for the system in this driver. + */ + ili->conf = device_get_match_data(dev); + if (!ili->conf) { + dev_err(dev, "missing device configuration\n"); + return -ENODEV; + } + + ili->dev = dev; + ili->max_spi_speed = ili->conf->max_spi_speed; + ili->vpl.node = dev->of_node; + ili->vpl.ioctl = ili9341_ioctl; + + return vpl_register(&ili->vpl); +} + +static int ili9341_probe(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct gpio_desc *dc, *reset; + + reset = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset)) + dev_err(dev, "Failed to get gpio 'reset'\n"); + + dc = gpiod_get(dev, "dc", GPIOD_OUT_LOW); + if (IS_ERR(dc)) + dev_err(dev, "Failed to get gpio 'dc'\n"); + + return ili9341_dpi_probe(spi, dc, reset); +} + +static const struct of_device_id ili9341_of_match[] = { + { + .compatible = "st,sf-tc240t-9370-t", + .data = &ili9341_stm32f429_disco_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, ili9341_of_match); + +static struct driver ili9341_driver = { + .name = "panel-ilitek-ili9341", + .of_compatible = ili9341_of_match, + .probe = ili9341_probe, +}; +device_spi_driver(ili9341_driver); + +MODULE_AUTHOR("Dillon Min <dillon.minfei@gmail.com>"); +MODULE_DESCRIPTION("ILI9341 LCD panel driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/panel-mipi-dbi.c b/drivers/video/panel-mipi-dbi.c new file mode 100644 index 0000000000..fecb232796 --- /dev/null +++ b/drivers/video/panel-mipi-dbi.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DRM driver for MIPI DBI compatible display panels + * + * Copyright 2022 Noralf Trønnes + */ + +#include <clock.h> +#include <common.h> +#include <fb.h> +#include <firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/printk.h> +#include <of.h> +#include <regulator.h> +#include <spi/spi.h> + +#include <video/backlight.h> +#include <video/mipi_dbi.h> +#include <video/mipi_display.h> + +static const u8 panel_mipi_dbi_magic[15] = { 'M', 'I', 'P', 'I', ' ', 'D', 'B', 'I', + 0, 0, 0, 0, 0, 0, 0 }; + +/* + * The display controller configuration is stored in a firmware file. + * The Device Tree 'compatible' property value with a '.bin' suffix is passed + * to request_firmware() to fetch this file. + */ +struct panel_mipi_dbi_config { + /* Magic string: panel_mipi_dbi_magic */ + u8 magic[15]; + + /* Config file format version */ + u8 file_format_version; + + /* + * MIPI commands to execute when the display pipeline is enabled. + * This is used to configure the display controller. + * + * The commands are stored in a byte array with the format: + * command, num_parameters, [ parameter, ...], command, ... + * + * Some commands require a pause before the next command can be received. + * Inserting a delay in the command sequence is done by using the NOP command with one + * parameter: delay in miliseconds (the No Operation command is part of the MIPI Display + * Command Set where it has no parameters). + * + * Example: + * command 0x11 + * sleep 120ms + * command 0xb1 parameters 0x01, 0x2c, 0x2d + * command 0x29 + * + * Byte sequence: + * 0x11 0x00 + * 0x00 0x01 0x78 + * 0xb1 0x03 0x01 0x2c 0x2d + * 0x29 0x00 + */ + u8 commands[]; +}; + +struct panel_mipi_dbi_commands { + const u8 *buf; + size_t len; +}; + +static struct panel_mipi_dbi_commands * +panel_mipi_dbi_check_commands(struct device *dev, const struct firmware *fw) +{ + const struct panel_mipi_dbi_config *config = (struct panel_mipi_dbi_config *)fw->data; + struct panel_mipi_dbi_commands *commands; + size_t size = fw->size, commands_len; + unsigned int i = 0; + + if (size < sizeof(*config) + 2) { /* At least 1 command */ + dev_err(dev, "config: file size=%zu is too small\n", size); + return ERR_PTR(-EINVAL); + } + + if (memcmp(config->magic, panel_mipi_dbi_magic, sizeof(config->magic))) { + dev_err(dev, "config: Bad magic: %15ph\n", config->magic); + return ERR_PTR(-EINVAL); + } + + if (config->file_format_version != 1) { + dev_err(dev, "config: version=%u is not supported\n", config->file_format_version); + return ERR_PTR(-EINVAL); + } + + dev_dbg(dev, "size=%zu version=%u\n", size, config->file_format_version); + + commands_len = size - sizeof(*config); + + while ((i + 1) < commands_len) { + u8 command = config->commands[i++]; + u8 num_parameters = config->commands[i++]; + const u8 *parameters = &config->commands[i]; + + i += num_parameters; + if (i > commands_len) { + dev_err(dev, "config: command=0x%02x num_parameters=%u overflows\n", + command, num_parameters); + return ERR_PTR(-EINVAL); + } + + if (command == 0x00 && num_parameters == 1) + dev_dbg(dev, "sleep %ums\n", parameters[0]); + else + dev_dbg(dev, "command %02x %*ph\n", + command, num_parameters, parameters); + } + + if (i != commands_len) { + dev_err(dev, "config: malformed command array\n"); + return ERR_PTR(-EINVAL); + } + + commands = kzalloc(sizeof(*commands), GFP_KERNEL); + if (!commands) + return ERR_PTR(-ENOMEM); + + commands->len = commands_len; + commands->buf = kmemdup(config->commands, commands->len, GFP_KERNEL); + if (!commands->buf) + return ERR_PTR(-ENOMEM); + + return commands; +} + +static struct panel_mipi_dbi_commands *panel_mipi_dbi_commands_from_fw(struct device *dev) +{ + struct panel_mipi_dbi_commands *commands; + const struct firmware *fw; + const char *compatible; + char fw_name[40]; + int ret; + + ret = of_property_read_string_index(dev->of_node, "compatible", 0, &compatible); + if (ret) + return ERR_PTR(ret); + + snprintf(fw_name, sizeof(fw_name), "%s.bin", compatible); + ret = request_firmware(&fw, fw_name, dev); + if (ret) { + dev_err(dev, "No config file found for compatible '%s' (error=%d)\n", + compatible, ret); + + return ERR_PTR(ret); + } + + commands = panel_mipi_dbi_check_commands(dev, fw); + release_firmware(fw); + + return commands; +} + +static void panel_mipi_dbi_commands_execute(struct mipi_dbi *dbi, + struct panel_mipi_dbi_commands *commands) +{ + unsigned int i = 0; + + if (!commands) + return; + + while (i < commands->len) { + u8 command = commands->buf[i++]; + u8 num_parameters = commands->buf[i++]; + const u8 *parameters = &commands->buf[i]; + + if (command == 0x00 && num_parameters == 1) + mdelay(parameters[0]); + else if (num_parameters) + mipi_dbi_command_stackbuf(dbi, command, parameters, num_parameters); + else + mipi_dbi_command(dbi, command); + + i += num_parameters; + } +} + +static void panel_mipi_dbi_enable(struct fb_info *info) +{ + struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info); + struct mipi_dbi *dbi = &dbidev->dbi; + int ret; + + if (!info->mode) { + dev_err(dbidev->dev, "No valid mode found\n"); + return; + } + + if (dbidev->backlight_node && !dbidev->backlight) { + dbidev->backlight = of_backlight_find(dbidev->backlight_node); + if (!dbidev->backlight) + dev_err(dbidev->dev, "No backlight found\n"); + } + + if (!dbidev->driver_private) { + dbidev->driver_private = panel_mipi_dbi_commands_from_fw(dbidev->dev); + if (IS_ERR(dbidev->driver_private)) { + dbidev->driver_private = NULL; + return; + } + } + + ret = mipi_dbi_poweron_conditional_reset(dbidev); + if (ret < 0) + return; + if (!ret) + panel_mipi_dbi_commands_execute(dbi, dbidev->driver_private); + + mipi_dbi_enable_flush(dbidev, info); +} + + +static struct fb_ops panel_mipi_dbi_ops = { + .fb_enable = panel_mipi_dbi_enable, + .fb_disable = mipi_dbi_fb_disable, + .fb_damage = mipi_dbi_fb_damage, + .fb_flush = mipi_dbi_fb_flush, +}; + + +static int panel_mipi_dbi_get_mode(struct mipi_dbi_dev *dbidev, struct fb_videomode *mode) +{ + struct device *dev = dbidev->dev; + int ret; + + ret = of_get_display_timing(dev->of_node, "panel-timing", mode); + if (ret) { + dev_err(dev, "%pOF: failed to get panel-timing (error=%d)\n", dev->of_node, ret); + return ret; + } + + /* + * Make sure width and height are set and that only back porch and + * pixelclock are set in the other timing values. Also check that + * width and height don't exceed the 16-bit value specified by MIPI DCS. + */ + if (!mode->xres || !mode->yres || mode->display_flags || + mode->right_margin || mode->hsync_len || (mode->left_margin + mode->xres) > 0xffff || + mode->lower_margin || mode->vsync_len || (mode->upper_margin + mode->yres) > 0xffff) { + dev_err(dev, "%pOF: panel-timing out of bounds\n", dev->of_node); + return -EINVAL; + } + + /* The driver doesn't use the pixel clock but it is mandatory so fake one if not set */ + if (!mode->pixclock) { + mode->pixclock = + (mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len) * + (mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len) * + 60 / 1000; + } + + return 0; +} + +static int panel_mipi_dbi_spi_probe(struct device *dev) +{ + struct mipi_dbi_dev *dbidev; + struct spi_device *spi = to_spi_device(dev); + struct mipi_dbi *dbi; + struct fb_info *info; + struct gpio_desc *dc; + int ret; + + dbidev = kzalloc(sizeof(*dbidev), GFP_KERNEL); + if (!dbidev) + return -ENOMEM; + + dbidev->dev = dev; + dbi = &dbidev->dbi; + info = &dbidev->info; + + ret = panel_mipi_dbi_get_mode(dbidev, &dbidev->mode); + if (ret) + return ret; + + dbidev->regulator = regulator_get(dev, "power"); + if (IS_ERR(dbidev->regulator)) + return dev_err_probe(dev, PTR_ERR(dbidev->regulator), + "Failed to get regulator 'power'\n"); + + dbidev->io_regulator = regulator_get(dev, "io"); + if (IS_ERR(dbidev->io_regulator)) + return dev_err_probe(dev, PTR_ERR(dbidev->io_regulator), + "Failed to get regulator 'io'\n"); + + dbidev->backlight_node = of_parse_phandle(dev->of_node, "backlight", 0); + + dbi->reset = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(dbi->reset)) + return dev_errp_probe(dev, dbi->reset, + "Failed to get GPIO 'reset'\n"); + + dc = gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); + if (IS_ERR(dc)) + return dev_errp_probe(dev, dc, "Failed to get GPIO 'dc'\n"); + + ret = mipi_dbi_spi_init(spi, dbi, dc); + if (ret) + return ret; + + ret = mipi_dbi_dev_init(dbidev, &panel_mipi_dbi_ops, &dbidev->mode); + if (ret) + return ret; + + ret = register_framebuffer(info); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to register framebuffer\n"); + + return 0; +} + +static const struct of_device_id panel_mipi_dbi_spi_of_match[] = { + { .compatible = "panel-mipi-dbi-spi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, panel_mipi_dbi_spi_of_match); + +static struct driver panel_mipi_dbi_spi_driver = { + .name = "panel-mipi-dbi-spi", + .probe = panel_mipi_dbi_spi_probe, + .of_compatible = DRV_OF_COMPAT(panel_mipi_dbi_spi_of_match), +}; +device_spi_driver(panel_mipi_dbi_spi_driver); + +MODULE_DESCRIPTION("MIPI DBI compatible display panel driver"); +MODULE_AUTHOR("Noralf Trønnes"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/pxa.c b/drivers/video/pxa.c index d444e0981f..561a73fb32 100644 --- a/drivers/video/pxa.c +++ b/drivers/video/pxa.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2010 Marc Kleine-Budde, Pengutronix * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * 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. - * * Derived from the linux-2.6 pxa framebuffer driver: * * Copyright (C) 1999 Eric A. Thomas. @@ -32,13 +23,13 @@ #include <malloc.h> #include <linux/err.h> -#include <mach/clock.h> -#include <mach/pxa-regs.h> -#include <mach/regs-lcd.h> -#include <mach/pxafb.h> +#include <mach/pxa/clock.h> +#include <mach/pxa/pxa-regs.h> +#include <mach/pxa/regs-lcd.h> +#include <mach/pxa/pxafb.h> #include <asm/io.h> -#include <asm-generic/div64.h> +#include <linux/math64.h> /* PXA LCD DMA descriptor */ struct pxafb_dma_descriptor { @@ -83,7 +74,7 @@ struct pxafb_info { struct pxafb_videomode *mode; struct fb_info info; - struct device_d *dev; + struct device *dev; void (*lcd_power)(int); void (*backlight_power)(int); @@ -485,7 +476,7 @@ static struct fb_ops pxafb_ops = { .fb_disable = pxafb_disable_controller, }; -static int pxafb_probe(struct device_d *dev) +static int pxafb_probe(struct device *dev) { struct resource *iores; struct pxafb_platform_data *pdata = dev->platform_data; @@ -533,6 +524,7 @@ static int pxafb_probe(struct device_d *dev) pxafb_activate_var(fbi); + fbi->info.dev.parent = dev; ret = register_framebuffer(&fbi->info); if (ret < 0) { dev_err(dev, "failed to register framebuffer\n"); @@ -542,7 +534,7 @@ static int pxafb_probe(struct device_d *dev) return 0; } -static struct driver_d pxafb_driver = { +static struct driver pxafb_driver = { .name = "pxafb", .probe = pxafb_probe, }; diff --git a/drivers/video/ramfb.c b/drivers/video/ramfb.c new file mode 100644 index 0000000000..3442e81b9e --- /dev/null +++ b/drivers/video/ramfb.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: (C) 2022 Adrian Negreanu + +#define pr_fmt(fmt) "ramfb: " fmt + +#include <common.h> +#include <fb.h> +#include <fcntl.h> +#include <dma.h> +#include <init.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <fs.h> +#include <linux/qemu_fw_cfg.h> +#include <video/fourcc.h> + +struct ramfb { + int fd; + struct fb_info info; + dma_addr_t screen_dma; + struct fb_videomode mode; + u16 etcfb_select; +}; + +struct fw_cfg_etc_ramfb { + u64 addr; + u32 fourcc; + u32 flags; + u32 width; + u32 height; + u32 stride; +} __packed; + +static int fw_cfg_find_file(struct device *dev, int fd, const char *filename) +{ + size_t filename_len = strlen(filename); + ssize_t ret; + __be32 count; + int i; + + ioctl(fd, FW_CFG_SELECT, &(u16) { FW_CFG_FILE_DIR }); + + lseek(fd, 0, SEEK_SET); + + ret = read(fd, &count, sizeof(count)); + if (ret < 0) + return ret; + + for (i = 0; i < be32_to_cpu(count); i++) { + struct fw_cfg_file qfile; + + read(fd, &qfile, sizeof(qfile)); + + dev_dbg(dev, "enumerating file %s\n", qfile.name); + + if (memcmp(qfile.name, filename, filename_len)) + continue; + + return be16_to_cpu(qfile.select); + } + + return -ENOENT; +} + +static void ramfb_populate_modes(struct ramfb *ramfb) +{ + struct fb_info *info = &ramfb->info; + + ramfb->mode.name = "x8r8g8b8"; + info->xres = ramfb->mode.xres = 640; + info->yres = ramfb->mode.yres = 480; + + info->mode = &ramfb->mode; + info->bits_per_pixel = 32; + info->red = (struct fb_bitfield) {16, 8}; + info->green = (struct fb_bitfield) {8, 8}; + info->blue = (struct fb_bitfield) {0, 8}; + info->transp = (struct fb_bitfield) {0, 0}; +} + +static int ramfb_activate_var(struct fb_info *fbi) +{ + struct ramfb *ramfb = fbi->priv; + + if (fbi->screen_base) + dma_free_coherent(fbi->screen_base, ramfb->screen_dma, fbi->screen_size); + + fbi->screen_size = fbi->xres * fbi->yres * fbi->bits_per_pixel; + fbi->screen_base = dma_alloc_coherent(fbi->screen_size, &ramfb->screen_dma); + + return 0; +} + +static void ramfb_enable(struct fb_info *fbi) +{ + struct ramfb *ramfb = fbi->priv; + struct fw_cfg_etc_ramfb *etc_ramfb; + + etc_ramfb = dma_alloc(sizeof(*etc_ramfb)); + + etc_ramfb->addr = cpu_to_be64(ramfb->screen_dma); + etc_ramfb->fourcc = cpu_to_be32(DRM_FORMAT_XRGB8888); + etc_ramfb->flags = cpu_to_be32(0); + etc_ramfb->width = cpu_to_be32(fbi->xres); + etc_ramfb->height = cpu_to_be32(fbi->yres); + etc_ramfb->stride = cpu_to_be32(fbi->line_length); + + ioctl(ramfb->fd, FW_CFG_SELECT, &ramfb->etcfb_select); + + pwrite(ramfb->fd, etc_ramfb, sizeof(*etc_ramfb), 0); + + dma_free(etc_ramfb); +} + +static struct fb_ops ramfb_ops = { + .fb_activate_var = ramfb_activate_var, + .fb_enable = ramfb_enable, +}; + +static int ramfb_probe(struct device *parent_dev, int fd) +{ + int ret; + struct ramfb *ramfb; + struct fb_info *fbi; + + ret = -ENODEV; + + ramfb = xzalloc(sizeof(*ramfb)); + + ramfb->fd = fd; + + ret = fw_cfg_find_file(parent_dev, fd, "etc/ramfb"); + if (ret < 0) { + dev_dbg(parent_dev, "ramfb: fw_cfg (etc/ramfb) file not found\n"); + return -ENODEV; + } + + ramfb->etcfb_select = ret; + dev_dbg(parent_dev, "etc/ramfb file at slot 0x%x\n", ramfb->etcfb_select); + + fbi = &ramfb->info; + fbi->priv = ramfb; + fbi->fbops = &ramfb_ops; + fbi->dev.parent = parent_dev; + + ramfb_populate_modes(ramfb); + + ret = register_framebuffer(fbi); + if (ret < 0) { + dev_err(parent_dev, "Unable to register ramfb: %d\n", ret); + return ret; + } + + dev_info(parent_dev, "ramfb registered\n"); + + return 0; +} + +static int ramfb_driver_init(void) +{ + struct cdev *cdev; + int err = 0; + + for_each_cdev(cdev) { + int fd, ret; + + if (!strstarts(cdev->name, "fw_cfg")) + continue; + + fd = cdev_fdopen(cdev, O_RDWR); + if (fd < 0) { + err = fd; + continue; + } + + ret = ramfb_probe(cdev->dev, fd); + if (ret == 0) + continue; + if (ret != -ENODEV && ret != -ENXIO) + err = ret; + + close(fd); + } + + return err; +} +device_initcall(ramfb_driver_init); + +MODULE_AUTHOR("Adrian Negreanu <adrian.negreanu@nxp.com>"); +MODULE_DESCRIPTION("QEMU RamFB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/rave-sp-backlight.c b/drivers/video/rave-sp-backlight.c index 877a5feeb6..360b27eebb 100644 --- a/drivers/video/rave-sp-backlight.c +++ b/drivers/video/rave-sp-backlight.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * LCD Backlight driver for RAVE SP * * Copyright (C) 2018 Zodiac Inflight Innovations - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <common.h> @@ -39,7 +27,7 @@ static int rave_sp_backlight_set(struct backlight_device *bd, int brightness) return rave_sp_exec(sp, cmd, sizeof(cmd), NULL, 0); } -static int rave_sp_backlight_probe(struct device_d *dev) +static int rave_sp_backlight_probe(struct device *dev) { struct backlight_device *bd; int ret; @@ -64,8 +52,9 @@ static const struct of_device_id rave_sp_backlight_of_match[] = { { .compatible = "zii,rave-sp-backlight" }, {} }; +MODULE_DEVICE_TABLE(of, rave_sp_backlight_of_match); -static struct driver_d rave_sp_backlight_driver = { +static struct driver rave_sp_backlight_driver = { .name = "rave-sp-backlight", .probe = rave_sp_backlight_probe, .of_compatible = DRV_OF_COMPAT(rave_sp_backlight_of_match), diff --git a/drivers/video/s3c24xx.c b/drivers/video/s3c24xx.c deleted file mode 100644 index 84ed0aee39..0000000000 --- a/drivers/video/s3c24xx.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2010 Juergen Beisert - * Copyright (C) 2011 Alexey Galakhov - * - * This driver is based on a patch found in the web: - * (C) Copyright 2006 by OpenMoko, Inc. - * Author: Harald Welte <laforge at openmoko.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <common.h> -#include <init.h> -#include <fb.h> -#include <driver.h> -#include <malloc.h> -#include <errno.h> -#include <io.h> -#include <mach/s3c-generic.h> -#include <mach/s3c24xx-fb.h> - -#define LCDCON1 0x00 -# define PNRMODE(x) (((x) & 3) << 5) -# define BPPMODE(x) (((x) & 0xf) << 1) -# define SET_CLKVAL(x) (((x) & 0x3ff) << 8) -# define GET_CLKVAL(x) (((x) >> 8) & 0x3ff) -# define ENVID (1 << 0) - -#define LCDCON2 0x04 -# define SET_VBPD(x) (((x) & 0xff) << 24) -# define SET_LINEVAL(x) (((x) & 0x3ff) << 14) -# define SET_VFPD(x) (((x) & 0xff) << 6) -# define SET_VSPW(x) ((x) & 0x3f) - -#define LCDCON3 0x08 -# define SET_HBPD(x) (((x) & 0x7f) << 19) -# define SET_HOZVAL(x) (((x) & 0x7ff) << 8) -# define SET_HFPD(x) ((x) & 0xff) - -#define LCDCON4 0x0c -# define SET_HSPW(x) ((x) & 0xff) - -#define LCDCON5 0x10 -# define BPP24BL (1 << 12) -# define FRM565 (1 << 11) -# define INV_CLK (1 << 10) -# define INV_HS (1 << 9) -# define INV_VS (1 << 8) -# define INV_DTA (1 << 7) -# define INV_DE (1 << 6) -# define INV_PWREN (1 << 5) -# define INV_LEND (1 << 4) -# define ENA_PWREN (1 << 3) -# define ENA_LEND (1 << 2) -# define BSWP (1 << 1) -# define HWSWP (1 << 0) - -#define LCDSADDR1 0x14 -# define SET_LCDBANK(x) (((x) & 0x1ff) << 21) -# define GET_LCDBANK(x) (((x) >> 21) & 0x1ff) -# define SET_LCDBASEU(x) ((x) & 0x1fffff) -# define GET_LCDBASEU(x) ((x) & 0x1fffff) - -#define LCDSADDR2 0x18 -# define SET_LCDBASEL(x) ((x) & 0x1fffff) -# define GET_LCDBASEL(x) ((x) & 0x1fffff) - -#define LCDSADDR3 0x1c -# define SET_OFFSIZE(x) (((x) & 0x7ff) << 11) -# define GET_OFFSIZE(x) (((x) >> 11) & 0x7ff) -# define SET_PAGE_WIDTH(x) ((x) & 0x3ff) -# define GET_PAGE_WIDTH(x) ((x) & 0x3ff) - -#define RED_LUT 0x20 -#define GREEN_LUT 0x24 -#define BLUE_LUT 0x28 - -#define DITHMODE 0x4c - -#define TPAL 0x50 - -#define LCDINTPND 0x54 -#define LCDSRCPND 0x58 -#define LCDINTMSK 0x5c -# define FIWSEL (1 << 2) -# define INT_FrSyn (1 << 1) -# define INT_FiCnt (1 << 0) - -#define TCONSEL 0x60 - -#define RED 0 -#define GREEN 1 -#define BLUE 2 -#define TRANSP 3 - -struct s3cfb_info { - void __iomem *base; - unsigned memory_size; - struct fb_info info; - struct device_d *hw_dev; - int passive_display; - void (*enable)(int enable); -}; - -/* the RGB565 true colour mode */ -static const struct fb_bitfield def_rgb565[] = { - [RED] = { - .offset = 11, - .length = 5, - }, - [GREEN] = { - .offset = 5, - .length = 6, - }, - [BLUE] = { - .offset = 0, - .length = 5, - }, - [TRANSP] = { /* no support for transparency */ - .length = 0, - } -}; - -/* the RGB888 true colour mode */ -static const struct fb_bitfield def_rgb888[] = { - [RED] = { - .offset = 16, - .length = 8, - }, - [GREEN] = { - .offset = 8, - .length = 8, - }, - [BLUE] = { - .offset = 0, - .length = 8, - }, - [TRANSP] = { /* no support for transparency */ - .length = 0, - } -}; - -/** - * @param fb_info Framebuffer information - */ -static void s3cfb_enable_controller(struct fb_info *fb_info) -{ - struct s3cfb_info *fbi = fb_info->priv; - uint32_t con1; - - con1 = readl(fbi->base + LCDCON1); - - con1 |= ENVID; - - writel(con1, fbi->base + LCDCON1); - - if (fbi->enable) - fbi->enable(1); -} - -/** - * @param fb_info Framebuffer information - */ -static void s3cfb_disable_controller(struct fb_info *fb_info) -{ - struct s3cfb_info *fbi = fb_info->priv; - uint32_t con1; - - if (fbi->enable) - fbi->enable(0); - - con1 = readl(fbi->base + LCDCON1); - - con1 &= ~ENVID; - - writel(con1, fbi->base + LCDCON1); -} - -/** - * Prepare the video hardware for a specified video mode - * @param fb_info Framebuffer information - * @return 0 on success - */ -static int s3cfb_activate_var(struct fb_info *fb_info) -{ - struct s3cfb_info *fbi = fb_info->priv; - struct fb_videomode *mode = fb_info->mode; - unsigned size, hclk, div; - uint32_t con1, con2, con3, con4, con5 = 0; - - if (fbi->passive_display != 0) { - dev_err(fbi->hw_dev, "Passive displays are currently not supported\n"); - return -EINVAL; - } - - /* - * we need at least this amount of memory for the framebuffer - */ - size = mode->xres * mode->yres * (fb_info->bits_per_pixel >> 3); - if (fbi->memory_size != size || fb_info->screen_base == NULL) { - if (fb_info->screen_base) - free(fb_info->screen_base); - fbi->memory_size = 0; - fb_info->screen_base = malloc(size); - if (! fb_info->screen_base) - return -ENOMEM; - memset(fb_info->screen_base, 0, size); - fbi->memory_size = size; - } - - /* ensure video output is _off_ */ - writel(0x00000000, fbi->base + LCDCON1); - - hclk = s3c_get_hclk() / 1000U; /* hclk in kHz */ - div = hclk / PICOS2KHZ(mode->pixclock); - if (div < 3) - div = 3; - /* pixel clock is: (hclk) / ((div + 1) * 2) */ - div += 1; - div >>= 1; - div -= 1; - - con1 = PNRMODE(3) | SET_CLKVAL(div); /* PNRMODE=3 is TFT */ - - switch (fb_info->bits_per_pixel) { - case 16: - con1 |= BPPMODE(12); - con5 |= FRM565; - con5 |= HWSWP; - fb_info->red = def_rgb565[RED]; - fb_info->green = def_rgb565[GREEN]; - fb_info->blue = def_rgb565[BLUE]; - fb_info->transp = def_rgb565[TRANSP]; - break; - case 24: - con1 |= BPPMODE(13); - /* con5 |= BPP24BL; */ /* FIXME maybe needed, check alignment */ - fb_info->red = def_rgb888[RED]; - fb_info->green = def_rgb888[GREEN]; - fb_info->blue = def_rgb888[BLUE]; - fb_info->transp = def_rgb888[TRANSP]; - break; - default: - dev_err(fbi->hw_dev, "Invalid bits per pixel value: %u\n", fb_info->bits_per_pixel); - return -EINVAL; - } - - /* 'normal' in register description means positive logic */ - if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT)) - con5 |= INV_HS; - if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT)) - con5 |= INV_VS; - if (!(mode->sync & FB_SYNC_DE_HIGH_ACT)) - con5 |= INV_DE; - if (mode->sync & FB_SYNC_CLK_INVERT) - con5 |= INV_CLK; /* display should latch at the rising edge */ - if (mode->sync & FB_SYNC_DATA_INVERT) - con5 |= INV_DTA; - if (mode->sync & FB_SYNC_INVERT_PWREN) - con5 |= INV_PWREN; - if (mode->sync & FB_SYNC_INVERT_LEND) - con5 |= INV_LEND; - if (mode->sync & FB_SYNC_USE_PWREN) - con5 |= ENA_PWREN; /* FIXME should this be done conditionally/later? */ - if (mode->sync & FB_SYNC_USE_LEND) - con5 |= ENA_LEND; - if (mode->sync & FB_SYNC_SWAP_BYTES) - con5 ^= BSWP; - if (mode->sync & FB_SYNC_SWAP_HW) - con5 ^= HWSWP; - - /* vertical timing */ - con2 = SET_VBPD(mode->upper_margin - 1) | - SET_LINEVAL(mode->yres - 1) | - SET_VFPD(mode->lower_margin - 1) | - SET_VSPW(mode->vsync_len - 1); - - /* horizontal timing */ - con3 = SET_HBPD(mode->left_margin - 1) | - SET_HOZVAL(mode->xres - 1) | - SET_HFPD(mode->right_margin - 1); - con4 = SET_HSPW(mode->hsync_len - 1); - - /* basic timing setup */ - writel(con1, fbi->base + LCDCON1); - dev_dbg(fbi->hw_dev, "writing %08X into %p (con1)\n", con1, fbi->base + LCDCON1); - writel(con2, fbi->base + LCDCON2); - dev_dbg(fbi->hw_dev, "writing %08X into %p (con2)\n", con2, fbi->base + LCDCON2); - writel(con3, fbi->base + LCDCON3); - dev_dbg(fbi->hw_dev, "writing %08X into %p (con3)\n", con3, fbi->base + LCDCON3); - writel(con4, fbi->base + LCDCON4); - dev_dbg(fbi->hw_dev, "writing %08X into %p (con4)\n", con4, fbi->base + LCDCON4); - writel(con5, fbi->base + LCDCON5); - dev_dbg(fbi->hw_dev, "writing %08X into %p (con5)\n", con5, fbi->base + LCDCON5); - - dev_dbg(fbi->hw_dev, "setting up the fb baseadress to %p\n", fb_info->screen_base); - - /* framebuffer memory setup */ - writel((unsigned)fb_info->screen_base >> 1, fbi->base + LCDSADDR1); - size = mode->xres * (fb_info->bits_per_pixel >> 3) * (mode->yres); - writel(SET_LCDBASEL(((unsigned)fb_info->screen_base + size) >> 1), fbi->base + LCDSADDR2); - writel(SET_OFFSIZE(0) | - SET_PAGE_WIDTH((mode->xres * fb_info->bits_per_pixel) >> 4), - fbi->base + LCDSADDR3); - writel(FIWSEL | INT_FrSyn | INT_FiCnt, fbi->base + LCDINTMSK); - - return 0; -} - -/** - * Print some information about the current hardware state - * @param hw_dev S3C video device - */ -static void s3cfb_info(struct device_d *hw_dev) -{ - uint32_t con1, addr1, addr2, addr3; - struct s3cfb_info *fbi = hw_dev->priv; - - con1 = readl(fbi->base + LCDCON1); - addr1 = readl(fbi->base + LCDSADDR1); - addr2 = readl(fbi->base + LCDSADDR2); - addr3 = readl(fbi->base + LCDSADDR3); - - printf(" Video hardware info:\n"); - printf(" Video clock is running at %u Hz\n", s3c_get_hclk() / ((GET_CLKVAL(con1) + 1) * 2)); - printf(" Video memory bank starts at 0x%08X\n", GET_LCDBANK(addr1) << 22); - printf(" Video memory bank offset: 0x%08X\n", GET_LCDBASEU(addr1)); - printf(" Video memory end: 0x%08X\n", GET_LCDBASEU(addr2)); - printf(" Virtual screen offset size: %u half words\n", GET_OFFSIZE(addr3)); - printf(" Virtual screen page width: %u half words\n", GET_PAGE_WIDTH(addr3)); -} - -/* - * There is only one video hardware instance available. - * It makes no sense to dynamically allocate this data - */ -static struct fb_ops s3cfb_ops = { - .fb_activate_var = s3cfb_activate_var, - .fb_enable = s3cfb_enable_controller, - .fb_disable = s3cfb_disable_controller, -}; - -static struct s3cfb_info fbi = { - .info = { - .fbops = &s3cfb_ops, - }, -}; - -static int s3cfb_probe(struct device_d *hw_dev) -{ - struct resource *iores; - struct s3c_fb_platform_data *pdata = hw_dev->platform_data; - int ret; - - if (! pdata) - return -ENODEV; - - iores = dev_request_mem_resource(hw_dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - fbi.base = IOMEM(iores->start); - writel(0, fbi.base + LCDCON1); - writel(0, fbi.base + LCDCON5); /* FIXME not 0 for some displays */ - - /* just init */ - fbi.info.priv = &fbi; - - /* add runtime hardware info */ - fbi.hw_dev = hw_dev; - hw_dev->priv = &fbi; - - /* add runtime video info */ - fbi.info.modes.modes = pdata->mode_list; - fbi.info.modes.num_modes = pdata->mode_cnt; - fbi.info.mode = &fbi.info.modes.modes[0]; - fbi.info.xres = fbi.info.mode->xres; - fbi.info.yres = fbi.info.mode->yres; - if (pdata->bits_per_pixel) - fbi.info.bits_per_pixel = pdata->bits_per_pixel; - else - fbi.info.bits_per_pixel = 16; - fbi.passive_display = pdata->passive_display; - fbi.enable = pdata->enable; - - if (IS_ENABLED(CONFIG_DRIVER_VIDEO_S3C_VERBOSE)) - hw_dev->info = s3cfb_info; - - ret = register_framebuffer(&fbi.info); - if (ret != 0) { - dev_err(hw_dev, "Failed to register framebuffer\n"); - return -EINVAL; - } - - return 0; -} - -static struct driver_d s3cfb_driver = { - .name = "s3c_fb", - .probe = s3cfb_probe, -}; -device_platform_driver(s3cfb_driver); - -/** - * The S3C244x LCD controller supports passive (CSTN/STN) and active (TFT) LC displays - * - * The driver itself currently supports only active TFT LC displays in the follwing manner: - * - * * True colours - * - 16 bpp - * - 24 bpp (untested) - */ diff --git a/drivers/video/sdl.c b/drivers/video/sdl.c index 8f5b409efb..06e13b7735 100644 --- a/drivers/video/sdl.c +++ b/drivers/video/sdl.c @@ -1,7 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * GPL v2 */ #include <common.h> @@ -13,23 +12,25 @@ #include <errno.h> #include <gui/graphic_utils.h> +#define to_mask(color) GENMASK(color.length - 1, color.offset) + static void sdlfb_enable(struct fb_info *info) { - int ret; - - ret = sdl_open(info->xres, info->yres, info->bits_per_pixel, - info->screen_base); - if (ret) - return; - sdl_get_bitfield_rgba(&info->red, &info->green, &info->blue, &info->transp); - - sdl_start_timer(); + struct sdl_fb_info sdl_info = { + .screen_base = info->screen_base, + .xres = info->xres, .yres = info->yres, .bpp = info->bits_per_pixel, + .rmask = to_mask(info->red), + .gmask = to_mask(info->green), + .bmask = to_mask(info->blue), + .amask = to_mask(info->transp), + }; + + sdl_video_open(&sdl_info); } static void sdlfb_disable(struct fb_info *info) { - sdl_stop_timer(); - sdl_close(); + sdl_video_close(); } static struct fb_ops sdlfb_ops = { @@ -37,7 +38,7 @@ static struct fb_ops sdlfb_ops = { .fb_disable = sdlfb_disable, }; -static int sdlfb_probe(struct device_d *dev) +static int sdlfb_probe(struct device *dev) { struct fb_info *fb; int ret = -EIO; @@ -48,10 +49,19 @@ static int sdlfb_probe(struct device_d *dev) fb = xzalloc(sizeof(*fb)); fb->modes.modes = fb->mode = dev->platform_data; fb->modes.num_modes = 1; - fb->bits_per_pixel = 4 << 3; fb->xres = fb->mode->xres; fb->yres = fb->mode->yres; + fb->bits_per_pixel = 32; + fb->transp.length = 8; + fb->red.length = 8; + fb->green.length = 8; + fb->blue.length = 8; + fb->transp.offset = 24; + fb->red.offset = 16; + fb->green.offset = 8; + fb->blue.offset = 0; + fb->priv = fb; fb->fbops = &sdlfb_ops; @@ -59,15 +69,6 @@ static int sdlfb_probe(struct device_d *dev) fb->screen_base = xzalloc(fb->xres * fb->yres * fb->bits_per_pixel >> 3); - dev_dbg(dev, "red: length = %d, offset = %d\n", - fb->red.length, fb->red.offset); - dev_dbg(dev, "green: length = %d, offset = %d\n", - fb->green.length, fb->green.offset); - dev_dbg(dev, "blue: length = %d, offset = %d\n", - fb->blue.length, fb->blue.offset); - dev_dbg(dev, "transp: length = %d, offset = %d\n", - fb->transp.length, fb->transp.offset); - /* add runtime hardware info */ dev->priv = fb; @@ -77,20 +78,18 @@ static int sdlfb_probe(struct device_d *dev) kfree(fb->screen_base); kfree(fb); - sdl_close(); return ret; } -static void sdlfb_remove(struct device_d *dev) +static void sdlfb_remove(struct device *dev) { struct fb_info *fb = dev->priv; kfree(fb->screen_base); kfree(fb); - sdl_close(); } -static struct driver_d sdlfb_driver = { +static struct driver sdlfb_driver = { .name = "sdlfb", .probe = sdlfb_probe, .remove = sdlfb_remove, diff --git a/drivers/video/simple-panel.c b/drivers/video/simple-panel.c index be39ff0e94..7048e2f51b 100644 --- a/drivers/video/simple-panel.c +++ b/drivers/video/simple-panel.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * simple panel support for barebox * * (C) Copyright 2014 Sascha Hauer, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <common.h> #include <malloc.h> @@ -30,7 +17,7 @@ #include <i2c/i2c.h> struct simple_panel { - struct device_d *dev; + struct device *dev; struct vpl vpl; int enable_gpio; int enable_active_high; @@ -112,7 +99,7 @@ static int simple_panel_get_modes(struct simple_panel *panel, struct display_tim } } - modes = of_get_display_timings(panel->dev->device_node); + modes = of_get_display_timings(panel->dev->of_node); if (modes) { timings->modes = modes->modes; timings->num_modes = modes->num_modes; @@ -138,14 +125,14 @@ static int simple_panel_ioctl(struct vpl *vpl, unsigned int port, case VPL_GET_VIDEOMODES: return simple_panel_get_modes(panel, ptr); default: - return -ENOSYS; + return 0; } } -static int simple_panel_probe(struct device_d *dev) +static int simple_panel_probe(struct device *dev) { struct simple_panel *panel; - struct device_node *node = dev->device_node; + struct device_node *node = dev->of_node; enum of_gpio_flags flags; int ret; @@ -178,8 +165,9 @@ static struct of_device_id simple_panel_of_ids[] = { { .compatible = "simple-panel", }, { } }; +MODULE_DEVICE_TABLE(of, simple_panel_of_ids); -static struct driver_d simple_panel_driver = { +static struct driver simple_panel_driver = { .name = "simple-panel", .probe = simple_panel_probe, .of_compatible = DRV_OF_COMPAT(simple_panel_of_ids), diff --git a/drivers/video/simplefb-client.c b/drivers/video/simplefb-client.c new file mode 100644 index 0000000000..dafec6178f --- /dev/null +++ b/drivers/video/simplefb-client.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Simplest possible simple frame-buffer driver, as a platform device + * + * Copyright (c) 2013, Stephen Warren + * + * Based on q40fb.c, which was: + * Copyright (C) 2001 Richard Zidlicky <rz@linux-m68k.org> + * + * Also based on offb.c, which was: + * Copyright (C) 1997 Geert Uytterhoeven + * Copyright (C) 1996 Paul Mackerras + */ + +#include <common.h> +#include <fb.h> +#include <io.h> +#include <linux/platform_data/simplefb.h> +#include <driver.h> +#include <of.h> + +static struct fb_ops simplefb_ops; + +static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; + +struct simplefb_params { + u32 width; + u32 height; + u32 stride; + struct simplefb_format *format; +}; + +struct simplefb { + struct fb_info info; + struct fb_videomode mode; +}; + +static int simplefb_parse_dt(struct device *dev, + struct simplefb_params *params) +{ + struct device_node *np = dev->of_node; + int ret; + const char *format; + int i; + + ret = of_property_read_u32(np, "width", ¶ms->width); + if (ret) { + dev_err(dev, "Can't parse width property\n"); + return ret; + } + + ret = of_property_read_u32(np, "height", ¶ms->height); + if (ret) { + dev_err(dev, "Can't parse height property\n"); + return ret; + } + + ret = of_property_read_u32(np, "stride", ¶ms->stride); + if (ret) { + dev_err(dev, "Can't parse stride property\n"); + return ret; + } + + ret = of_property_read_string(np, "format", &format); + if (ret) { + dev_err(dev, "Can't parse format property\n"); + return ret; + } + params->format = NULL; + for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { + if (strcmp(format, simplefb_formats[i].name)) + continue; + params->format = &simplefb_formats[i]; + break; + } + if (!params->format) { + dev_err(dev, "Invalid format value\n"); + return -EINVAL; + } + + return 0; +} + +static int simplefb_probe(struct device *dev) +{ + int ret; + struct simplefb_params params; + struct simplefb *simplefb; + struct fb_info *info; + struct resource *mem; + + ret = -ENODEV; + if (dev->of_node) + ret = simplefb_parse_dt(dev, ¶ms); + + if (ret) + return ret; + + mem = dev_request_mem_resource(dev, 0); + if (IS_ERR(mem)) { + dev_err(dev, "No memory resource\n"); + return PTR_ERR(mem); + } + + simplefb = xzalloc(sizeof(*simplefb)); + + simplefb->mode.name = params.format->name; + simplefb->mode.xres = params.width; + simplefb->mode.yres = params.height; + + info = &simplefb->info; + info->mode = &simplefb->mode; + info->bits_per_pixel = params.format->bits_per_pixel; + info->red = params.format->red; + info->green = params.format->green; + info->blue = params.format->blue; + info->transp = params.format->transp; + + info->screen_base = (void *)mem->start; + info->screen_size = resource_size(mem); + + + info->fbops = &simplefb_ops; + + info->dev.parent = dev; + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(dev, "Unable to register simplefb: %d\n", ret); + return ret; + } + + dev_info(dev, "size %s registered\n", size_human_readable(info->screen_size)); + + return 0; +} + +static const struct of_device_id simplefb_of_match[] = { + { .compatible = "simple-framebuffer", }, + { }, +}; +MODULE_DEVICE_TABLE(of, simplefb_of_match); + +static struct driver simplefb_driver = { + .name = "simple-framebuffer", + .of_compatible = simplefb_of_match, + .probe = simplefb_probe, +}; +device_platform_driver(simplefb_driver); + +MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>"); +MODULE_DESCRIPTION("Simple framebuffer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb-fixup.c index a2c59de364..65e0281a18 100644 --- a/drivers/video/simplefb.c +++ b/drivers/video/simplefb-fixup.c @@ -90,6 +90,7 @@ static int simplefb_create_node(struct device_node *root, const struct fb_info *fbi, const char *format) { struct device_node *node; + phys_addr_t screen_base; u32 cells[2]; int ret; @@ -105,7 +106,11 @@ static int simplefb_create_node(struct device_node *root, if (ret) return ret; - cells[0] = cpu_to_be32((u32)fbi->screen_base); + screen_base = virt_to_phys(fbi->screen_base); + if (upper_32_bits(screen_base)) + return -ENOSYS; + + cells[0] = cpu_to_be32(lower_32_bits(screen_base)); cells[1] = cpu_to_be32(fbi->line_length * fbi->yres); ret = of_set_property(node, "reg", cells, sizeof(cells[0]) * 2, 1); if (ret < 0) @@ -130,8 +135,7 @@ static int simplefb_create_node(struct device_node *root, if (ret < 0) return ret; - of_add_reserve_entry((u32)fbi->screen_base, - (u32)fbi->screen_base + fbi->screen_size); + of_add_reserve_entry(screen_base, screen_base + fbi->screen_size); return of_property_write_string(node, "status", "okay"); } diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c index 835814bf53..fb50e895c5 100644 --- a/drivers/video/ssd1307fb.c +++ b/drivers/video/ssd1307fb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for the Solomon SSD1307 OLED controller family * @@ -13,8 +14,6 @@ * * Ported to barebox from linux v4.10 * Copyright (C) 2017 Pengutronix, Bastian Stender <kernel@pengutronix.de> - * - * Licensed under the GPLv2 or later. */ #include <common.h> @@ -24,6 +23,7 @@ #include <gpio.h> #include <of_gpio.h> #include <regulator.h> +#include <spi/spi.h> #define SSD1307FB_DATA 0x40 #define SSD1307FB_COMMAND 0x80 @@ -54,6 +54,16 @@ struct ssd1307fb_deviceinfo { int need_chargepump; }; +struct ssd1307fb_array { + u8 type; + u8 data[0]; +}; + +struct ssd1307fb_par; + +typedef int (*ssd1307fb_write_array)(struct ssd1307fb_par *par, + struct ssd1307fb_array *array, u32 len); + struct ssd1307fb_par { u32 com_invdir; u32 com_lrremap; @@ -64,21 +74,20 @@ struct ssd1307fb_par { u32 dclk_frq; const struct ssd1307fb_deviceinfo *device_info; struct i2c_client *client; + struct spi_device *spi; u32 height; struct fb_info *info; u32 page_offset; u32 prechargep1; u32 prechargep2; int reset; + int dc; struct regulator *vbat; u32 seg_remap; u32 vcomh; u32 width; -}; -struct ssd1307fb_array { - u8 type; - u8 data[0]; + ssd1307fb_write_array write_array; }; static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type) @@ -94,9 +103,31 @@ static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type) return array; } -static int ssd1307fb_write_array(struct i2c_client *client, - struct ssd1307fb_array *array, u32 len) +static int ssd1307fb_spi_write_array(struct ssd1307fb_par *par, + struct ssd1307fb_array *array, u32 len) +{ + struct spi_device *spi = par->spi; + int ret; + + if (array->type == SSD1307FB_COMMAND) + gpio_direction_output(par->dc, 0); + else + gpio_direction_output(par->dc, 1); + + ret = spi_write(spi, array->data, len); + if (ret) + dev_err(&spi->dev, "Couldn't send SPI command.\n"); + + /* Ensure that we remain in data mode. */ + gpio_direction_output(par->dc, 1); + + return ret; +} + +static int ssd1307fb_i2c_write_array(struct ssd1307fb_par *par, + struct ssd1307fb_array *array, u32 len) { + struct i2c_client *client = par->client; int ret; len += sizeof(struct ssd1307fb_array); @@ -110,7 +141,7 @@ static int ssd1307fb_write_array(struct i2c_client *client, return 0; } -static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) +static inline int ssd1307fb_write_cmd(struct ssd1307fb_par *par, u8 cmd) { struct ssd1307fb_array *array; int ret; @@ -121,7 +152,7 @@ static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) array->data[0] = cmd; - ret = ssd1307fb_write_array(client, array, 1); + ret = par->write_array(par, array, 1); kfree(array); return ret; @@ -182,20 +213,20 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par) } } - ssd1307fb_write_array(par->client, array, par->width * par->height / 8); + par->write_array(par, array, par->width * par->height / 8); kfree(array); } static void ssd1307fb_enable(struct fb_info *info) { struct ssd1307fb_par *par = info->priv; - ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); + ssd1307fb_write_cmd(par, SSD1307FB_DISPLAY_ON); } static void ssd1307fb_disable(struct fb_info *info) { struct ssd1307fb_par *par = info->priv; - ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF); + ssd1307fb_write_cmd(par, SSD1307FB_DISPLAY_OFF); } static void ssd1307fb_flush(struct fb_info *info) @@ -216,134 +247,134 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) u32 precharge, dclk, com_invdir, compins; /* Set initial contrast */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST); + ret = ssd1307fb_write_cmd(par, SSD1307FB_CONTRAST); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, par->contrast); + ret = ssd1307fb_write_cmd(par, par->contrast); if (ret < 0) return ret; /* Set segment re-map */ if (par->seg_remap) { - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SEG_REMAP_ON); if (ret < 0) return ret; }; /* Set COM direction */ com_invdir = 0xc0 | (par->com_invdir & 0x1) << 3; - ret = ssd1307fb_write_cmd(par->client, com_invdir); + ret = ssd1307fb_write_cmd(par, com_invdir); if (ret < 0) return ret; /* Set multiplex ratio value */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_MULTIPLEX_RATIO); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, par->height - 1); + ret = ssd1307fb_write_cmd(par, par->height - 1); if (ret < 0) return ret; /* set display offset value */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_DISPLAY_OFFSET); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, par->com_offset); + ret = ssd1307fb_write_cmd(par, par->com_offset); if (ret < 0) return ret; /* Set clock frequency */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_CLOCK_FREQ); if (ret < 0) return ret; dclk = ((par->dclk_div - 1) & 0xf) | (par->dclk_frq & 0xf) << 4; - ret = ssd1307fb_write_cmd(par->client, dclk); + ret = ssd1307fb_write_cmd(par, dclk); if (ret < 0) return ret; /* Set precharge period in number of ticks from the internal clock */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_PRECHARGE_PERIOD); if (ret < 0) return ret; precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4; - ret = ssd1307fb_write_cmd(par->client, precharge); + ret = ssd1307fb_write_cmd(par, precharge); if (ret < 0) return ret; /* Set COM pins configuration */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_COM_PINS_CONFIG); if (ret < 0) return ret; compins = 0x02 | !(par->com_seq & 0x1) << 4 | (par->com_lrremap & 0x1) << 5; - ret = ssd1307fb_write_cmd(par->client, compins); + ret = ssd1307fb_write_cmd(par, compins); if (ret < 0) return ret; /* Set VCOMH */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_VCOMH); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, par->vcomh); + ret = ssd1307fb_write_cmd(par, par->vcomh); if (ret < 0) return ret; /* Turn on the DC-DC Charge Pump */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP); + ret = ssd1307fb_write_cmd(par, SSD1307FB_CHARGE_PUMP); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, + ret = ssd1307fb_write_cmd(par, BIT(4) | (par->device_info->need_chargepump ? BIT(2) : 0)); if (ret < 0) return ret; /* Switch to horizontal addressing mode */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_ADDRESS_MODE); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); if (ret < 0) return ret; /* Set column range */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_COL_RANGE); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, 0x0); + ret = ssd1307fb_write_cmd(par, 0x0); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, par->width - 1); + ret = ssd1307fb_write_cmd(par, par->width - 1); if (ret < 0) return ret; /* Set page range */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); + ret = ssd1307fb_write_cmd(par, SSD1307FB_SET_PAGE_RANGE); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, 0x0); + ret = ssd1307fb_write_cmd(par, 0x0); if (ret < 0) return ret; - ret = ssd1307fb_write_cmd(par->client, + ret = ssd1307fb_write_cmd(par, par->page_offset + (par->height / 8) - 1); if (ret < 0) return ret; /* Turn on the display */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); + ret = ssd1307fb_write_cmd(par, SSD1307FB_DISPLAY_ON); if (ret < 0) return ret; @@ -379,28 +410,37 @@ static const struct of_device_id ssd1307fb_of_match[] = { .data = (void *)&ssd1307fb_ssd1306_deviceinfo, }, { + /* + * The compatible of the SPI connected ssd1306 is not + * documented as device tree binding. + */ + .compatible = "solomon,ssd1306", + .data = (void *)&ssd1307fb_ssd1306_deviceinfo, + }, + { .compatible = "solomon,ssd1309fb-i2c", .data = (void *)&ssd1307fb_ssd1309_deviceinfo, }, {}, }; +MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); -static int ssd1307fb_probe(struct device_d *dev) +static int ssd1307fb_probe(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); struct fb_info *info; - struct device_node *node = dev->device_node; + struct device_node *node = dev->of_node; const struct of_device_id *match = - of_match_node(ssd1307fb_of_match, dev->device_node); + of_match_node(ssd1307fb_of_match, dev->of_node); u32 vmem_size; struct ssd1307fb_par *par; struct ssd1307fb_array *array; - u8 *vmem; + u8 *vmem = NULL; + enum of_gpio_flags of_flags; int ret; int i, j; if (!node) { - dev_err(&client->dev, "No device tree data found!\n"); + dev_err(dev, "No device tree data found!\n"); return -EINVAL; } @@ -409,33 +449,47 @@ static int ssd1307fb_probe(struct device_d *dev) info->priv = par; par->info = info; - par->client = client; par->device_info = (struct ssd1307fb_deviceinfo *)match->data; - par->reset = of_get_named_gpio(node, - "reset-gpios", 0); + if (dev_bus_is_i2c(dev)) { + par->client = to_i2c_client(dev); + i2c_set_clientdata(par->client, par); + par->write_array = ssd1307fb_i2c_write_array; + } + if (dev_bus_is_spi(dev)) { + par->spi = to_spi_device(dev); + par->dc = of_get_named_gpio(node, "dc-gpios", 0); + if (!gpio_is_valid(par->dc)) { + ret = par->dc; + goto fb_alloc_error; + } + par->write_array = ssd1307fb_spi_write_array; + } + + par->reset = of_get_named_gpio_flags(node, + "reset-gpios", 0, &of_flags); if (!gpio_is_valid(par->reset) && par->reset == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto fb_alloc_error; } - par->vbat = regulator_get(&client->dev, "vbat-supply"); + par->vbat = regulator_get(dev, "vbat"); if (IS_ERR(par->vbat)) { - dev_info(&client->dev, "Will not use VBAT"); + dev_info(dev, "Will not use VBAT"); par->vbat = NULL; } ret = of_property_read_u32(node, "solomon,width", &par->width); if (ret) { - dev_err(&client->dev, + dev_err(dev, "Couldn't find 'solomon,width' in device tree.\n"); goto panel_init_error; } ret = of_property_read_u32(node, "solomon,height", &par->height); if (ret) { - dev_err(&client->dev, + dev_err(dev, "Couldn't find 'solomon,height' in device tree.\n"); goto panel_init_error; } @@ -443,7 +497,7 @@ static int ssd1307fb_probe(struct device_d *dev) ret = of_property_read_u32(node, "solomon,page-offset", &par->page_offset); if (ret) { - dev_err(&client->dev, + dev_err(dev, "Couldn't find 'solomon,page_offset' in device tree.\n"); goto panel_init_error; } @@ -476,7 +530,7 @@ static int ssd1307fb_probe(struct device_d *dev) vmem = malloc(vmem_size); if (!vmem) { - dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); + dev_err(dev, "Couldn't allocate graphical memory.\n"); ret = -ENOMEM; goto fb_alloc_error; } @@ -500,11 +554,14 @@ static int ssd1307fb_probe(struct device_d *dev) info->screen_base = (u8 __force __iomem *)vmem; if (par->reset >= 0) { - ret = gpio_request_one(par->reset, - GPIOF_OUT_INIT_HIGH, - "oled-reset"); + unsigned long flags = GPIOF_OUT_INIT_ACTIVE; + + if (of_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + ret = gpio_request_one(par->reset, flags, "oled-reset"); if (ret) { - dev_err(&client->dev, + dev_err(dev, "failed to request gpio %d: %d\n", par->reset, ret); goto reset_oled_error; @@ -515,11 +572,9 @@ static int ssd1307fb_probe(struct device_d *dev) if (ret < 0) goto reset_oled_error; - i2c_set_clientdata(client, info); - if (par->reset > 0) { /* Reset the screen */ - gpio_set_value(par->reset, 0); + gpio_set_active(par->reset, 1); udelay(4); } @@ -531,7 +586,7 @@ static int ssd1307fb_probe(struct device_d *dev) mdelay(100); if (par->reset > 0) { - gpio_set_value(par->reset, 1); + gpio_set_active(par->reset, 0); udelay(4); } @@ -539,9 +594,10 @@ static int ssd1307fb_probe(struct device_d *dev) if (ret) goto reset_oled_error; + info->dev.parent = dev; ret = register_framebuffer(info); if (ret) { - dev_err(&client->dev, "Couldn't register the framebuffer\n"); + dev_err(dev, "Couldn't register the framebuffer\n"); goto panel_init_error; } @@ -560,10 +616,10 @@ static int ssd1307fb_probe(struct device_d *dev) } } - ssd1307fb_write_array(par->client, array, par->width * par->height / 8); + par->write_array(par, array, par->width * par->height / 8); kfree(array); - dev_info(&client->dev, + dev_info(dev, "ssd1307 framebuffer device registered, using %d bytes of video memory\n", vmem_size); @@ -579,9 +635,16 @@ fb_alloc_error: return ret; } -static struct driver_d ssd1307fb_driver = { - .name = "ssd1307fb", +static __maybe_unused struct driver ssd1307fb_i2c_driver = { + .name = "ssd1307fb-i2c", + .probe = ssd1307fb_probe, + .of_compatible = DRV_OF_COMPAT(ssd1307fb_of_match), +}; +device_i2c_driver(ssd1307fb_i2c_driver); + +static __maybe_unused struct driver ssd1307fb_spi_driver = { + .name = "ssd1307fb-spi", .probe = ssd1307fb_probe, .of_compatible = DRV_OF_COMPAT(ssd1307fb_of_match), }; -device_i2c_driver(ssd1307fb_driver); +device_spi_driver(ssd1307fb_spi_driver); diff --git a/drivers/video/stm.c b/drivers/video/stm.c index d4a618fe50..917405ea80 100644 --- a/drivers/video/stm.c +++ b/drivers/video/stm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2010 Juergen Beisert, Pengutronix <jbe@pengutronix.de> * @@ -6,16 +7,6 @@ * * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Embedded Alley Solutions, Inc 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 - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * 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 <common.h> #include <init.h> @@ -29,7 +20,7 @@ #include <stmp-device.h> #include <linux/clk.h> #include <linux/err.h> -#include <mach/fb.h> +#include <mach/mxs/fb.h> #define HW_LCDIF_CTRL 0x00 # define CTRL_SFTRST (1 << 31) @@ -148,7 +139,7 @@ struct imxfb_info { void __iomem *base; unsigned memory_size; struct fb_info info; - struct device_d *hw_dev; + struct device *hw_dev; struct clk *clk; void *fixed_screen; unsigned fixed_screen_size; @@ -503,7 +494,7 @@ static struct imxfb_info fbi = { }, }; -static int stmfb_probe(struct device_d *hw_dev) +static int stmfb_probe(struct device *hw_dev) { struct resource *iores; struct imx_fb_platformdata *pdata = hw_dev->platform_data; @@ -541,10 +532,10 @@ static int stmfb_probe(struct device_d *hw_dev) struct display_timings *modes; struct device_node *display; - if (!IS_ENABLED(CONFIG_OFDEVICE) || !hw_dev->device_node) + if (!IS_ENABLED(CONFIG_OFDEVICE) || !hw_dev->of_node) return -EINVAL; - display = of_parse_phandle(hw_dev->device_node, "display", 0); + display = of_parse_phandle(hw_dev->of_node, "display", 0); if (!display) { dev_err(hw_dev, "no display phandle\n"); return -EINVAL; @@ -571,6 +562,7 @@ static int stmfb_probe(struct device_d *hw_dev) fb_of_reserve_add_fixup(&fbi.info); + fbi.info.dev.parent = hw_dev; ret = register_framebuffer(&fbi.info); if (ret != 0) { dev_err(hw_dev, "Failed to register framebuffer\n"); @@ -589,8 +581,9 @@ static __maybe_unused struct of_device_id stmfb_compatible[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, stmfb_compatible); -static struct driver_d stmfb_driver = { +static struct driver stmfb_driver = { .name = "stmfb", .probe = stmfb_probe, .of_compatible = DRV_OF_COMPAT(stmfb_compatible), diff --git a/drivers/video/stm32_ltdc.c b/drivers/video/stm32_ltdc.c new file mode 100644 index 0000000000..d1c36b1f45 --- /dev/null +++ b/drivers/video/stm32_ltdc.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017-2018 STMicroelectronics - All Rights Reserved + * Author(s): Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics. + * Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics. + * Ahmad Fatoum <a.fatoum@pengutronix.de> + */ + +#include <common.h> +#include <init.h> +#include <linux/clk.h> +#include <linux/reset.h> +#include <io.h> +#include <fb.h> +#include <dma.h> +#include <video/media-bus-format.h> +#include <video/vpl.h> +#include <of_graph.h> + +#include "stm32_ltdc.h" + +struct ltdc_hw { + void __iomem *regs; + struct device *dev; + struct clk *pclk; + bool claimed; +}; + +struct ltdc_fb { + int id; + struct fb_info info; + u32 bg_col_argb; + u32 alpha; + u32 bus_format; + enum stm32_ltdc_pixfmt pixfmt; + struct vpl vpl; + struct ltdc_hw *hw; +}; + +static bool has_alpha(enum stm32_ltdc_pixfmt pixfmt) +{ + switch (pixfmt) { + case PF_ARGB8888: + case PF_ARGB1555: + case PF_ARGB4444: + case PF_AL44: + case PF_AL88: + return true; + case PF_RGB888: + case PF_RGB565: + case PF_L8: + default: + return false; + } +} + +static void ltdc_set_mode(struct ltdc_fb *priv, + struct fb_videomode *mode) +{ + void __iomem *regs = priv->hw->regs; + u32 hsync, vsync, acc_hbp, acc_vbp, acc_act_w, acc_act_h; + u32 total_w, total_h; + u32 val; + + /* Convert video timings to ltdc timings */ + hsync = mode->hsync_len - 1; + vsync = mode->vsync_len - 1; + acc_hbp = hsync + mode->left_margin; + acc_vbp = vsync + mode->upper_margin; + acc_act_w = acc_hbp + mode->xres; + acc_act_h = acc_vbp + mode->yres; + total_w = acc_act_w + mode->right_margin; + total_h = acc_act_h + mode->lower_margin; + + /* Synchronization sizes */ + val = (hsync << 16) | vsync; + clrsetbits_le32(regs + LTDC_SSCR, SSCR_VSH | SSCR_HSW, val); + + /* Accumulated back porch */ + val = (acc_hbp << 16) | acc_vbp; + clrsetbits_le32(regs + LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val); + + /* Accumulated active width */ + val = (acc_act_w << 16) | acc_act_h; + clrsetbits_le32(regs + LTDC_AWCR, AWCR_AAW | AWCR_AAH, val); + + /* Total width & height */ + val = (total_w << 16) | total_h; + clrsetbits_le32(regs + LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val); + + setbits_le32(regs + LTDC_LIPCR, acc_act_h + 1); + + /* Signal polarities */ + val = 0; + dev_dbg(priv->hw->dev, "mode->display_flags 0x%x mode->sync 0x%x\n", + mode->display_flags, mode->sync); + if (mode->sync & FB_SYNC_HOR_HIGH_ACT) + val |= GCR_HSPOL; + if (mode->sync & FB_SYNC_VERT_HIGH_ACT) + val |= GCR_VSPOL; + if (mode->display_flags & DISPLAY_FLAGS_DE_LOW) + val |= GCR_DEPOL; + if (mode->display_flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) + val |= GCR_PCPOL; + + clrsetbits_le32(regs + LTDC_GCR, + GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val); + + /* Overall background color */ + writel(priv->bg_col_argb, regs + LTDC_BCCR); +} + +static void ltdc_set_layer1(struct ltdc_fb *priv) +{ + void __iomem *regs = priv->hw->regs; + u32 x0, x1, y0, y1; + u32 pitch_in_bytes; + u32 line_length; + u32 bus_width; + u32 val, tmp, bpp; + struct fb_videomode *mode = priv->info.mode; + + x0 = y0 = 0; + x1 = mode->xres - 1; + y1 = mode->yres - 1; + + /* Horizontal start and stop position */ + tmp = (readl(regs + LTDC_BPCR) & BPCR_AHBP) >> 16; + val = ((x1 + 1 + tmp) << 16) + (x0 + 1 + tmp); + clrsetbits_le32(regs + LTDC_L1WHPCR, LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, + val); + + /* Vertical start & stop position */ + tmp = readl(regs + LTDC_BPCR) & BPCR_AVBP; + val = ((y1 + 1 + tmp) << 16) + (y0 + 1 + tmp); + clrsetbits_le32(regs + LTDC_L1WVPCR, LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, + val); + + /* Layer background color */ + writel(priv->bg_col_argb, regs + LTDC_L1DCCR); + + /* Color frame buffer pitch in bytes & line length */ + bpp = priv->info.bits_per_pixel; + pitch_in_bytes = mode->xres * (bpp >> 3); + bus_width = 8 << ((readl(regs + LTDC_GC2R) & GC2R_BW) >> 4); + line_length = ((bpp >> 3) * mode->xres) + (bus_width >> 3) - 1; + val = (pitch_in_bytes << 16) | line_length; + clrsetbits_le32(regs + LTDC_L1CFBLR, LXCFBLR_CFBLL | LXCFBLR_CFBP, val); + + /* Pixel format */ + clrsetbits_le32(regs + LTDC_L1PFCR, LXPFCR_PF, priv->pixfmt); + + /* Constant alpha value */ + clrsetbits_le32(regs + LTDC_L1CACR, LXCACR_CONSTA, priv->alpha); + + /* Specifies the blending factors : with or without pixel alpha */ + /* Manage hw-specific capabilities */ + val = has_alpha(priv->pixfmt) ? BF1_PAXCA | BF2_1PAXCA : BF1_CA | BF2_1CA; + + /* Blending factors */ + clrsetbits_le32(regs + LTDC_L1BFCR, LXBFCR_BF2 | LXBFCR_BF1, val); + + /* Frame buffer line number */ + clrsetbits_le32(regs + LTDC_L1CFBLNR, LXCFBLNR_CFBLN, mode->yres); + + /* Frame buffer address */ + writel((unsigned long)priv->info.screen_base, regs + LTDC_L1CFBAR); + + /* Enable layer 1 */ + setbits_le32(regs + LTDC_L1CR, LXCR_LEN); +} + +static int ltdc_activate_var(struct fb_info *info) +{ + info->line_length = info->xres * (info->bits_per_pixel >> 3); + + info->screen_base = dma_alloc_writecombine(info->line_length * info->yres, + DMA_ADDRESS_BROKEN); + if (!info->screen_base) + return -ENOMEM; + + return 0; +} + +static void ltdc_enable(struct fb_info *info) +{ + struct fb_videomode *mode = info->mode; + struct ltdc_fb *priv = info->priv; + struct ltdc_hw *hw = priv->hw; + u32 pixclock; + int ret; + + if (hw->claimed) { + dev_warn(hw->dev, "CRTC currently claimed by other frame buffer!\n"); + return; + } + + vpl_ioctl_prepare(&priv->vpl, priv->id, mode); + + pixclock = PICOS2KHZ(mode->pixclock) * 1000; + + ret = clk_enable(hw->pclk); + if (ret) { + dev_err(hw->dev, "peripheral clock enable error %d\n", ret); + return; + } + + clk_set_rate(clk_get_parent(hw->pclk), pixclock); + if (!ret) + ret = clk_set_rate(hw->pclk, pixclock); + if (ret < 0) { + dev_err(hw->dev, "fail to set pixel clock %d hz: %d\n", + pixclock, ret); + return; + } + + ret = device_reset_us(hw->dev, 100000); + if (ret) { + dev_err(hw->dev, "error resetting controller %d\n", ret); + return; + } + + /* Configure & start LTDC */ + ltdc_set_mode(priv, mode); + ltdc_set_layer1(priv); + + /* Reload configuration immediately & enable LTDC */ + setbits_le32(hw->regs + LTDC_SRCR, SRCR_IMR); + setbits_le32(hw->regs + LTDC_GCR, GCR_LTDCEN); + + vpl_ioctl_enable(&priv->vpl, priv->id); + + hw->claimed = true; +} + +static void ltdc_disable(struct fb_info *info) +{ + struct ltdc_fb *priv = info->priv; + + vpl_ioctl_disable(&priv->vpl, priv->id); + + clrbits_le32(priv->hw->regs + LTDC_GCR, GCR_LTDCEN); + clk_disable(priv->hw->pclk); + priv->hw->claimed = false; + + vpl_ioctl_unprepare(&priv->vpl, priv->id); +} + +static struct fb_ops ltdc_ops = { + .fb_activate_var = ltdc_activate_var, + .fb_enable = ltdc_enable, + .fb_disable = ltdc_disable, +}; + +static int ltdc_probe(struct device *dev) +{ + struct device_node *np; + struct resource *iores; + struct ltdc_hw *hw; + int ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + hw = xzalloc(sizeof *hw); + hw->dev = dev; + hw->regs = IOMEM(iores->start); + + hw->pclk = clk_get(dev, NULL); + if (IS_ERR(hw->pclk)) { + dev_err(dev, "peripheral clock get error %d\n", ret); + return PTR_ERR(hw->pclk); + } + + for_each_available_child_of_node(dev->of_node, np) { + struct ltdc_fb *priv; + struct of_endpoint ep; + struct fb_info *info; + + if (!of_graph_port_is_available(np)) + continue; + + ret = of_graph_parse_endpoint(np, &ep); + if (ret) + return ret; + + dev_dbg(hw->dev, "register vpl for %pOF\n", np); + + priv = xzalloc(sizeof(*priv)); + priv->hw = hw; + priv->id = ep.id; + priv->vpl.node = dev->of_node; + + ret = vpl_register(&priv->vpl); + if (ret) + return ret; + + info = &priv->info; + info->priv = priv; + info->fbops = <dc_ops; + + info->red = (struct fb_bitfield){ .offset = 11, .length = 5, }; + info->green = (struct fb_bitfield){ .offset = 5, .length = 6, }; + info->blue = (struct fb_bitfield){ .offset = 0, .length = 5, }; + info->bits_per_pixel = 16, + priv->pixfmt = PF_RGB565; + /* TODO Below parameters are hard-coded for the moment... */ + priv->bg_col_argb = 0xFFFFFFFF; /* white no transparency */ + priv->alpha = 0xFF; + + ret = vpl_ioctl(&priv->vpl, priv->id, VPL_GET_VIDEOMODES, &info->modes); + if (ret) + dev_dbg(dev, "failed to get modes: %s\n", strerror(-ret)); + + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(dev, "failed to register framebuffer\n"); + return ret; + } + } + + return 0; +} + +static __maybe_unused struct of_device_id ltdc_ids[] = { + { .compatible = "st,stm32-ltdc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ltdc_ids); + +static struct driver ltdc_driver = { + .name = "stm32-ltdc", + .probe = ltdc_probe, + .of_compatible = DRV_OF_COMPAT(ltdc_ids), +}; +device_platform_driver(ltdc_driver); diff --git a/drivers/video/stm32_ltdc.h b/drivers/video/stm32_ltdc.h new file mode 100644 index 0000000000..6481f2613b --- /dev/null +++ b/drivers/video/stm32_ltdc.h @@ -0,0 +1,130 @@ +#ifndef STM32_LTDC_H__ +#define STM32_LTDC_H__ + +/* LTDC main registers */ +#define LTDC_IDR 0x00 /* IDentification */ +#define LTDC_LCR 0x04 /* Layer Count */ +#define LTDC_SSCR 0x08 /* Synchronization Size Configuration */ +#define LTDC_BPCR 0x0C /* Back Porch Configuration */ +#define LTDC_AWCR 0x10 /* Active Width Configuration */ +#define LTDC_TWCR 0x14 /* Total Width Configuration */ +#define LTDC_GCR 0x18 /* Global Control */ +#define LTDC_GC1R 0x1C /* Global Configuration 1 */ +#define LTDC_GC2R 0x20 /* Global Configuration 2 */ +#define LTDC_SRCR 0x24 /* Shadow Reload Configuration */ +#define LTDC_GACR 0x28 /* GAmma Correction */ +#define LTDC_BCCR 0x2C /* Background Color Configuration */ +#define LTDC_IER 0x34 /* Interrupt Enable */ +#define LTDC_ISR 0x38 /* Interrupt Status */ +#define LTDC_ICR 0x3C /* Interrupt Clear */ +#define LTDC_LIPCR 0x40 /* Line Interrupt Position Conf. */ +#define LTDC_CPSR 0x44 /* Current Position Status */ +#define LTDC_CDSR 0x48 /* Current Display Status */ + +/* LTDC layer 1 registers */ +#define LTDC_L1LC1R 0x80 /* L1 Layer Configuration 1 */ +#define LTDC_L1LC2R 0x84 /* L1 Layer Configuration 2 */ +#define LTDC_L1CR 0x84 /* L1 Control */ +#define LTDC_L1WHPCR 0x88 /* L1 Window Hor Position Config */ +#define LTDC_L1WVPCR 0x8C /* L1 Window Vert Position Config */ +#define LTDC_L1CKCR 0x90 /* L1 Color Keying Configuration */ +#define LTDC_L1PFCR 0x94 /* L1 Pixel Format Configuration */ +#define LTDC_L1CACR 0x98 /* L1 Constant Alpha Config */ +#define LTDC_L1DCCR 0x9C /* L1 Default Color Configuration */ +#define LTDC_L1BFCR 0xA0 /* L1 Blend Factors Configuration */ +#define LTDC_L1FBBCR 0xA4 /* L1 FrameBuffer Bus Control */ +#define LTDC_L1AFBCR 0xA8 /* L1 AuxFB Control */ +#define LTDC_L1CFBAR 0xAC /* L1 Color FrameBuffer Address */ +#define LTDC_L1CFBLR 0xB0 /* L1 Color FrameBuffer Length */ +#define LTDC_L1CFBLNR 0xB4 /* L1 Color FrameBuffer Line Nb */ +#define LTDC_L1AFBAR 0xB8 /* L1 AuxFB Address */ +#define LTDC_L1AFBLR 0xBC /* L1 AuxFB Length */ +#define LTDC_L1AFBLNR 0xC0 /* L1 AuxFB Line Number */ +#define LTDC_L1CLUTWR 0xC4 /* L1 CLUT Write */ + +/* Bit definitions */ +#define SSCR_VSH GENMASK(10, 0) /* Vertical Synchronization Height */ +#define SSCR_HSW GENMASK(27, 16) /* Horizontal Synchronization Width */ + +#define BPCR_AVBP GENMASK(10, 0) /* Accumulated Vertical Back Porch */ +#define BPCR_AHBP GENMASK(27, 16) /* Accumulated Horizontal Back Porch */ + +#define AWCR_AAH GENMASK(10, 0) /* Accumulated Active Height */ +#define AWCR_AAW GENMASK(27, 16) /* Accumulated Active Width */ + +#define TWCR_TOTALH GENMASK(10, 0) /* TOTAL Height */ +#define TWCR_TOTALW GENMASK(27, 16) /* TOTAL Width */ + +#define GCR_LTDCEN BIT(0) /* LTDC ENable */ +#define GCR_DEN BIT(16) /* Dither ENable */ +#define GCR_PCPOL BIT(28) /* Pixel Clock POLarity-Inverted */ +#define GCR_DEPOL BIT(29) /* Data Enable POLarity-High */ +#define GCR_VSPOL BIT(30) /* Vertical Synchro POLarity-High */ +#define GCR_HSPOL BIT(31) /* Horizontal Synchro POLarity-High */ + +#define GC1R_WBCH GENMASK(3, 0) /* Width of Blue CHannel output */ +#define GC1R_WGCH GENMASK(7, 4) /* Width of Green Channel output */ +#define GC1R_WRCH GENMASK(11, 8) /* Width of Red Channel output */ +#define GC1R_PBEN BIT(12) /* Precise Blending ENable */ +#define GC1R_DT GENMASK(15, 14) /* Dithering Technique */ +#define GC1R_GCT GENMASK(19, 17) /* Gamma Correction Technique */ +#define GC1R_SHREN BIT(21) /* SHadow Registers ENabled */ +#define GC1R_BCP BIT(22) /* Background Colour Programmable */ +#define GC1R_BBEN BIT(23) /* Background Blending ENabled */ +#define GC1R_LNIP BIT(24) /* Line Number IRQ Position */ +#define GC1R_TP BIT(25) /* Timing Programmable */ +#define GC1R_IPP BIT(26) /* IRQ Polarity Programmable */ +#define GC1R_SPP BIT(27) /* Sync Polarity Programmable */ +#define GC1R_DWP BIT(28) /* Dither Width Programmable */ +#define GC1R_STREN BIT(29) /* STatus Registers ENabled */ +#define GC1R_BMEN BIT(31) /* Blind Mode ENabled */ + +#define GC2R_EDCA BIT(0) /* External Display Control Ability */ +#define GC2R_STSAEN BIT(1) /* Slave Timing Sync Ability ENabled */ +#define GC2R_DVAEN BIT(2) /* Dual-View Ability ENabled */ +#define GC2R_DPAEN BIT(3) /* Dual-Port Ability ENabled */ +#define GC2R_BW GENMASK(6, 4) /* Bus Width (log2 of nb of bytes) */ +#define GC2R_EDCEN BIT(7) /* External Display Control ENabled */ + +#define SRCR_IMR BIT(0) /* IMmediate Reload */ +#define SRCR_VBR BIT(1) /* Vertical Blanking Reload */ + +#define LXCR_LEN BIT(0) /* Layer ENable */ +#define LXCR_COLKEN BIT(1) /* Color Keying Enable */ +#define LXCR_CLUTEN BIT(4) /* Color Look-Up Table ENable */ + +#define LXWHPCR_WHSTPOS GENMASK(11, 0) /* Window Horizontal StarT POSition */ +#define LXWHPCR_WHSPPOS GENMASK(27, 16) /* Window Horizontal StoP POSition */ + +#define LXWVPCR_WVSTPOS GENMASK(10, 0) /* Window Vertical StarT POSition */ +#define LXWVPCR_WVSPPOS GENMASK(26, 16) /* Window Vertical StoP POSition */ + +#define LXPFCR_PF GENMASK(2, 0) /* Pixel Format */ + +#define LXCACR_CONSTA GENMASK(7, 0) /* CONSTant Alpha */ + +#define LXBFCR_BF2 GENMASK(2, 0) /* Blending Factor 2 */ +#define LXBFCR_BF1 GENMASK(10, 8) /* Blending Factor 1 */ + +#define LXCFBLR_CFBLL GENMASK(12, 0) /* Color Frame Buffer Line Length */ +#define LXCFBLR_CFBP GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */ + +#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */ + +#define BF1_PAXCA 0x600 /* Pixel Alpha x Constant Alpha */ +#define BF1_CA 0x400 /* Constant Alpha */ +#define BF2_1PAXCA 0x007 /* 1 - (Pixel Alpha x Constant Alpha) */ +#define BF2_1CA 0x005 /* 1 - Constant Alpha */ + +enum stm32_ltdc_pixfmt { + PF_ARGB8888 = 0, + PF_RGB888, + PF_RGB565, + PF_ARGB1555, + PF_ARGB4444, + PF_L8, + PF_AL44, + PF_AL88 +}; + +#endif diff --git a/drivers/video/tc358767.c b/drivers/video/tc358767.c index e64dde1ddf..da106496fc 100644 --- a/drivers/video/tc358767.c +++ b/drivers/video/tc358767.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * tc358767 eDP encoder driver * @@ -7,16 +8,6 @@ * Copyright (C) 2016 Pengutronix, Philipp Zabel <p.zabel@pengutronix.de> * * Copyright (C) 2016 Zodiac Inflight Innovations - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 <common.h> @@ -31,7 +22,7 @@ #include <of_gpio.h> #include <video/media-bus-format.h> #include <video/vpl.h> -#include <asm-generic/div64.h> +#include <linux/math64.h> #define DP_LINK_BW_SET 0x100 #define DP_ENHANCED_FRAME_EN (1 << 7) @@ -213,7 +204,7 @@ struct tc_edp_link { struct tc_data { struct i2c_client *client; - struct device_d *dev; + struct device *dev; /* DP AUX channel */ struct i2c_adapter adapter; struct vpl vpl; @@ -889,7 +880,7 @@ err: static int tc_main_link_setup(struct tc_data *tc) { - struct device_d *dev = tc->dev; + struct device *dev = tc->dev; unsigned int rate; u32 dp_phy_ctrl; int timeout; @@ -1191,8 +1182,7 @@ static int tc_read_edid(struct tc_data *tc) #ifdef DEBUG printk(KERN_DEBUG "eDP display EDID:\n"); - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, tc->edid, - EDID_LENGTH, true); + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, tc->edid, EDID_LENGTH); #endif return 0; @@ -1344,7 +1334,7 @@ static int tc_ioctl(struct vpl *vpl, unsigned int port, return ret; } -static int tc_probe(struct device_d *dev) +static int tc_probe(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct tc_data *tc; @@ -1357,16 +1347,16 @@ static int tc_probe(struct device_d *dev) tc->dev = dev; /* Shut down GPIO is optional */ - tc->sd_gpio = of_get_named_gpio_flags(dev->device_node, - "shutdown-gpios", 0, &flags); + tc->sd_gpio = of_get_named_gpio_flags(dev->of_node, + "shutdown-gpios", 0, &flags); if (gpio_is_valid(tc->sd_gpio)) { if (!(flags & OF_GPIO_ACTIVE_LOW)) tc->sd_active_high = 1; } /* Reset GPIO is optional */ - tc->reset_gpio = of_get_named_gpio_flags(dev->device_node, - "reset-gpios", 0, &flags); + tc->reset_gpio = of_get_named_gpio_flags(dev->of_node, + "reset-gpios", 0, &flags); if (gpio_is_valid(tc->reset_gpio)) { if (!(flags & OF_GPIO_ACTIVE_LOW)) tc->reset_active_high = 1; @@ -1381,7 +1371,7 @@ static int tc_probe(struct device_d *dev) gpio_direction_output(tc->sd_gpio, 0); } - tc->refclk = of_clk_get_by_name(dev->device_node, "ref"); + tc->refclk = of_clk_get_by_name(dev->of_node, "ref"); if (IS_ERR(tc->refclk)) { ret = PTR_ERR(tc->refclk); dev_err(dev, "Failed to get refclk: %d\n", ret); @@ -1420,7 +1410,7 @@ static int tc_probe(struct device_d *dev) } /* add vlp */ - tc->vpl.node = dev->device_node; + tc->vpl.node = dev->of_node; tc->vpl.ioctl = tc_ioctl; return vpl_register(&tc->vpl); @@ -1429,7 +1419,7 @@ err: return ret; } -static struct driver_d tc_driver = { +static struct driver tc_driver = { .name = "tc358767", .probe = tc_probe, }; diff --git a/drivers/video/vpl.c b/drivers/video/vpl.c index 82ceeebada..f8c2159cd9 100644 --- a/drivers/video/vpl.c +++ b/drivers/video/vpl.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Video pipeline (VPL) support for barebox * * (C) Copyright 2014 Sascha Hauer, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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) "VPL: " fmt @@ -31,7 +18,7 @@ int vpl_register(struct vpl *vpl) { list_add_tail(&vpl->list, &vpls); - pr_debug("%s: %s\n", __func__, vpl->node->full_name); + pr_debug("%s: %pOF\n", __func__, vpl->node); return 0; } @@ -53,13 +40,13 @@ struct vpl *of_vpl_get(struct device_node *node, int port) if (!node) return NULL; - pr_debug("%s: port: %s\n", __func__, node->full_name); + pr_debug("%s: port: %pOF\n", __func__, node); node = of_graph_get_remote_port_parent(node); if (!node) return NULL; - pr_debug("%s: remote port parent: %s\n", __func__, node->full_name); + pr_debug("%s: remote port parent: %pOF\n", __func__, node); return of_find_vpl(node); } @@ -70,11 +57,11 @@ int vpl_ioctl(struct vpl *vpl, unsigned int port, struct device_node *node, *endpoint; int ret; - pr_debug("%s: %s port %d\n", __func__, vpl->node->full_name, port); + pr_debug("%s: %pOF port %d\n", __func__, vpl->node, port); node = of_graph_get_port_by_id(vpl->node, port); if (!node) { - pr_err("%s: no port %d on %s\n", __func__, port, vpl->node->full_name); + pr_err("%s: no port %d on %pOF\n", __func__, port, vpl->node); return -ENODEV; } @@ -85,7 +72,7 @@ int vpl_ioctl(struct vpl *vpl, unsigned int port, remote = of_graph_get_remote_port(endpoint); if (!remote) { - pr_debug("%s: no remote for endpoint %s\n", __func__, endpoint->full_name); + pr_debug("%s: no remote for endpoint %pOF\n", __func__, endpoint); continue; } @@ -102,10 +89,11 @@ int vpl_ioctl(struct vpl *vpl, unsigned int port, remote_vpl = of_find_vpl(remote_parent); if (!remote_vpl) { - pr_debug("%s: cannot find remote vpl %s\n", __func__, remote->full_name); + pr_debug("%s: cannot find remote vpl %pOF\n", __func__, remote); continue; } + pr_debug("%s: looked up %pOF: %pS\n", __func__, remote, remote_vpl->ioctl); ret = remote_vpl->ioctl(remote_vpl, remote_port_id, cmd, ptr); if (ret) return ret; |