diff options
57 files changed, 2454 insertions, 752 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/leadtek,ltk500hd1829.yaml b/Documentation/devicetree/bindings/display/panel/leadtek,ltk500hd1829.yaml new file mode 100644 index 000000000000..4ebcea7d0c63 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/leadtek,ltk500hd1829.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/leadtek,ltk500hd1829.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Leadtek LTK500HD1829 5.0in 720x1280 DSI panel + +maintainers: + - Heiko Stuebner <heiko.stuebner@theobroma-systems.com> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: leadtek,ltk500hd1829 + reg: true + backlight: true + reset-gpios: true + iovcc-supply: + description: regulator that supplies the iovcc voltage + vcc-supply: + description: regulator that supplies the vcc voltage + +required: + - compatible + - reg + - backlight + - iovcc-supply + - vcc-supply + +additionalProperties: false + +examples: + - | + dsi@ff450000 { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "leadtek,ltk500hd1829"; + reg = <0>; + backlight = <&backlight>; + iovcc-supply = <&vcc_1v8>; + vcc-supply = <&vcc_2v8>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/xinpeng,xpp055c272.yaml b/Documentation/devicetree/bindings/display/panel/xinpeng,xpp055c272.yaml new file mode 100644 index 000000000000..186e5e1c8fa3 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/xinpeng,xpp055c272.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/xinpeng,xpp055c272.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xinpeng XPP055C272 5.5in 720x1280 DSI panel + +maintainers: + - Heiko Stuebner <heiko.stuebner@theobroma-systems.com> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: xinpeng,xpp055c272 + reg: true + backlight: true + reset-gpios: true + iovcc-supply: + description: regulator that supplies the iovcc voltage + vci-supply: + description: regulator that supplies the vci voltage + +required: + - compatible + - reg + - backlight + - iovcc-supply + - vci-supply + +additionalProperties: false + +examples: + - | + dsi@ff450000 { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "xinpeng,xpp055c272"; + reg = <0>; + backlight = <&backlight>; + iovcc-supply = <&vcc_1v8>; + vci-supply = <&vcc3v3_lcd>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-lvds.txt b/Documentation/devicetree/bindings/display/rockchip/rockchip-lvds.txt index 7849ff039229..aaf8c44cf90f 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip-lvds.txt +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-lvds.txt @@ -4,6 +4,7 @@ Rockchip RK3288 LVDS interface Required properties: - compatible: matching the soc type, one of - "rockchip,rk3288-lvds"; + - "rockchip,px30-lvds"; - reg: physical base address of the controller and length of memory mapped region. @@ -18,6 +19,9 @@ Required properties: - rockchip,grf: phandle to the general register files syscon - rockchip,output: "rgb", "lvds" or "duallvds", This describes the output interface +- phys: LVDS/DSI DPHY (px30 only) +- phy-names: name of the PHY, must be "dphy" (px30 only) + Optional properties: - pinctrl-names: must contain a "lcdc" entry. - pinctrl-0: pin control group to be used for this controller. diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 6046f4555852..4e6248ec5ed9 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -513,6 +513,8 @@ patternProperties: description: Lantiq Semiconductor "^lattice,.*": description: Lattice Semiconductor + "^leadtek,.*": + description: Shenzhen Leadtek Technology Co., Ltd. "^leez,.*": description: Leez "^lego,.*": @@ -1056,6 +1058,8 @@ patternProperties: description: Extreme Engineering Solutions (X-ES) "^xillybus,.*": description: Xillybus Ltd. + "^xinpeng,.*": + description: Shenzhen Xinpeng Technology Co., Ltd "^xlnx,.*": description: Xilinx "^xunlong,.*": diff --git a/Documentation/fb/fbcon.rst b/Documentation/fb/fbcon.rst index ebca41785abe..e57a3d1d085a 100644 --- a/Documentation/fb/fbcon.rst +++ b/Documentation/fb/fbcon.rst @@ -127,7 +127,7 @@ C. Boot options is typically located on the same video card. Thus, the consoles that are controlled by the VGA console will be garbled. -4. fbcon=rotate:<n> +5. fbcon=rotate:<n> This option changes the orientation angle of the console display. The value 'n' accepts the following: @@ -152,21 +152,21 @@ C. Boot options Actually, the underlying fb driver is totally ignorant of console rotation. -5. fbcon=margin:<color> +6. fbcon=margin:<color> This option specifies the color of the margins. The margins are the leftover area at the right and the bottom of the screen that are not used by text. By default, this area will be black. The 'color' value is an integer number that depends on the framebuffer driver being used. -6. fbcon=nodefer +7. fbcon=nodefer If the kernel is compiled with deferred fbcon takeover support, normally the framebuffer contents, left in place by the firmware/bootloader, will be preserved until there actually is some text is output to the console. This option causes fbcon to bind immediately to the fbdev device. -7. fbcon=logo-pos:<location> +8. fbcon=logo-pos:<location> The only possible 'location' is 'center' (without quotes), and when given, the bootup logo is moved from the default top-left corner @@ -174,6 +174,11 @@ C. Boot options displayed due to multiple CPUs, the collected line of logos is moved as a whole. +9. fbcon=logo-count:<n> + + The value 'n' overrides the number of bootup logos. 0 disables the + logo, and -1 gives the default which is the number of online CPUs. + C. Attaching, Detaching and Unloading Before going on to how to attach, detach and unload the framebuffer console, an diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index cde1cae073ec..34608f0499eb 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -1144,8 +1144,7 @@ static int ast_cursor_init(struct drm_device *dev) size = roundup(AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE, PAGE_SIZE); for (i = 0; i < ARRAY_SIZE(ast->cursor.gbo); ++i) { - gbo = drm_gem_vram_create(dev, &dev->vram_mm->bdev, - size, 0, false); + gbo = drm_gem_vram_create(dev, size, 0); if (IS_ERR(gbo)) { ret = PTR_ERR(gbo); goto err_drm_gem_vram_put; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index f2e73e6d46b8..10985134ce0b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -73,7 +73,11 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) unsigned long prate; unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL; unsigned int cfg = 0; - int div; + int div, ret; + + ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk); + if (ret) + return; vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end; @@ -95,14 +99,14 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) (adj->crtc_hdisplay - 1) | ((adj->crtc_vdisplay - 1) << 16)); + prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); + mode_rate = adj->crtc_clock * 1000; if (!crtc->dc->desc->fixed_clksrc) { + prate *= 2; cfg |= ATMEL_HLCDC_CLKSEL; mask |= ATMEL_HLCDC_CLKSEL; } - prate = 2 * clk_get_rate(crtc->dc->hlcdc->sys_clk); - mode_rate = adj->crtc_clock * 1000; - div = DIV_ROUND_UP(prate, mode_rate); if (div < 2) { div = 2; @@ -117,8 +121,8 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) int div_low = prate / mode_rate; if (div_low >= 2 && - ((prate / div_low - mode_rate) < - 10 * (mode_rate - prate / div))) + (10 * (prate / div_low - mode_rate) < + (mode_rate - prate / div))) /* * At least 10 times better when using a higher * frequency than requested, instead of a lower. @@ -147,6 +151,8 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK, cfg); + + clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); } static enum drm_mode_status diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 8dc917a1270b..112aa5066cee 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -721,18 +721,10 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) dc->hlcdc = dev_get_drvdata(dev->dev->parent); dev->dev_private = dc; - if (dc->desc->fixed_clksrc) { - ret = clk_prepare_enable(dc->hlcdc->sys_clk); - if (ret) { - dev_err(dev->dev, "failed to enable sys_clk\n"); - goto err_destroy_wq; - } - } - ret = clk_prepare_enable(dc->hlcdc->periph_clk); if (ret) { dev_err(dev->dev, "failed to enable periph_clk\n"); - goto err_sys_clk_disable; + goto err_destroy_wq; } pm_runtime_enable(dev->dev); @@ -768,9 +760,6 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) err_periph_clk_disable: pm_runtime_disable(dev->dev); clk_disable_unprepare(dc->hlcdc->periph_clk); -err_sys_clk_disable: - if (dc->desc->fixed_clksrc) - clk_disable_unprepare(dc->hlcdc->sys_clk); err_destroy_wq: destroy_workqueue(dc->wq); @@ -795,8 +784,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev) pm_runtime_disable(dev->dev); clk_disable_unprepare(dc->hlcdc->periph_clk); - if (dc->desc->fixed_clksrc) - clk_disable_unprepare(dc->hlcdc->sys_clk); destroy_workqueue(dc->wq); } @@ -910,8 +897,6 @@ static int atmel_hlcdc_dc_drm_suspend(struct device *dev) regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr); regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr); clk_disable_unprepare(dc->hlcdc->periph_clk); - if (dc->desc->fixed_clksrc) - clk_disable_unprepare(dc->hlcdc->sys_clk); return 0; } @@ -921,8 +906,6 @@ static int atmel_hlcdc_dc_drm_resume(struct device *dev) struct drm_device *drm_dev = dev_get_drvdata(dev); struct atmel_hlcdc_dc *dc = drm_dev->dev_private; - if (dc->desc->fixed_clksrc) - clk_prepare_enable(dc->hlcdc->sys_clk); clk_prepare_enable(dc->hlcdc->periph_clk); regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 6effe532f820..6fab71985cd4 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1289,19 +1289,21 @@ struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp, return conn_state->crtc; } -static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void +analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *old_state = old_bridge_state->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; int ret; - crtc = analogix_dp_get_new_crtc(dp, state); + crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) return; - old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); /* Don't touch the panel if we're coming back from PSR */ if (old_crtc_state && old_crtc_state->self_refresh_active) return; @@ -1366,20 +1368,22 @@ out_dp_clk_pre: return ret; } -static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void +analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *old_state = old_bridge_state->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; int timeout_loop = 0; int ret; - crtc = analogix_dp_get_new_crtc(dp, state); + crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) return; - old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc); /* Not a full enable, just disable PSR and continue */ if (old_crtc_state && old_crtc_state->self_refresh_active) { ret = analogix_dp_disable_psr(dp); @@ -1440,18 +1444,20 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge) dp->dpms_mode = DRM_MODE_DPMS_OFF; } -static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void +analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *old_state = old_bridge_state->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state = NULL; - crtc = analogix_dp_get_new_crtc(dp, state); + crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) goto out; - new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc); if (!new_crtc_state) goto out; @@ -1463,20 +1469,21 @@ out: analogix_dp_bridge_disable(bridge); } -static -void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) +static void +analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *old_state = old_bridge_state->base.state; struct analogix_dp_device *dp = bridge->driver_private; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state; int ret; - crtc = analogix_dp_get_new_crtc(dp, state); + crtc = analogix_dp_get_new_crtc(dp, old_state); if (!crtc) return; - new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc); if (!new_crtc_state || !new_crtc_state->self_refresh_active) return; diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c index 3a5bd4e7fd1e..b7c97f060241 100644 --- a/drivers/gpu/drm/bridge/cdns-dsi.c +++ b/drivers/gpu/drm/bridge/cdns-dsi.c @@ -512,7 +512,7 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi, struct cdns_dsi_output *output = &dsi->output; unsigned int tmp; bool sync_pulse = false; - int bpp, nlanes; + int bpp; memset(dsi_cfg, 0, sizeof(*dsi_cfg)); @@ -520,7 +520,6 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi, sync_pulse = true; bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format); - nlanes = output->dev->lanes; if (mode_valid_check) tmp = mode->htotal - @@ -785,13 +784,12 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) unsigned long tx_byte_period; struct cdns_dsi_cfg dsi_cfg; u32 tmp, reg_wakeup, div; - int bpp, nlanes; + int nlanes; if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) return; mode = &bridge->encoder->crtc->state->adjusted_mode; - bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format); nlanes = output->dev->lanes; WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false)); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c index 2b7539701b42..dd56996fe9c7 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c @@ -291,7 +291,7 @@ static irqreturn_t snd_dw_hdmi_irq(int irq, void *data) return IRQ_HANDLED; } -static struct snd_pcm_hardware dw_hdmi_hw = { +static const struct snd_pcm_hardware dw_hdmi_hw = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index d33691512a8e..bf1b9c37d515 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -30,6 +30,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_uapi.h> +#include <drm/drm_bridge.h> #include <drm/drm_debugfs.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> @@ -1018,6 +1019,44 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, } /** + * drm_atomic_add_encoder_bridges - add bridges attached to an encoder + * @state: atomic state + * @encoder: DRM encoder + * + * This function adds all bridges attached to @encoder. This is needed to add + * bridge states to @state and make them available when + * &bridge_funcs.atomic_{check,pre_enable,enable,disable_post_disable}() are + * called + * + * Returns: + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK + * then the w/w mutex code has detected a deadlock and the entire atomic + * sequence must be restarted. All other errors are fatal. + */ +int +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, + struct drm_encoder *encoder) +{ + struct drm_bridge_state *bridge_state; + struct drm_bridge *bridge; + + if (!encoder) + return 0; + + DRM_DEBUG_ATOMIC("Adding all bridges for [encoder:%d:%s] to %p\n", + encoder->base.id, encoder->name, state); + + drm_for_each_bridge_in_chain(encoder, bridge) { + bridge_state = drm_atomic_get_bridge_state(state, bridge); + if (IS_ERR(bridge_state)) + return PTR_ERR(bridge_state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_encoder_bridges); + +/** * drm_atomic_add_affected_connectors - add connectors for CRTC * @state: atomic state * @crtc: DRM CRTC diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 4511c2e07bb9..afe14f72a824 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -437,12 +437,12 @@ mode_fixup(struct drm_atomic_state *state) funcs = encoder->helper_private; bridge = drm_bridge_chain_get_first_bridge(encoder); - ret = drm_bridge_chain_mode_fixup(bridge, - &new_crtc_state->mode, - &new_crtc_state->adjusted_mode); - if (!ret) { - DRM_DEBUG_ATOMIC("Bridge fixup failed\n"); - return -EINVAL; + ret = drm_atomic_bridge_chain_check(bridge, + new_crtc_state, + new_conn_state); + if (ret) { + DRM_DEBUG_ATOMIC("Bridge atomic check failed\n"); + return ret; } if (funcs && funcs->atomic_check) { @@ -730,6 +730,26 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return ret; } + /* + * Iterate over all connectors again, and add all affected bridges to + * the state. + */ + for_each_oldnew_connector_in_state(state, connector, + old_connector_state, + new_connector_state, i) { + struct drm_encoder *encoder; + + encoder = old_connector_state->best_encoder; + ret = drm_atomic_add_encoder_bridges(state, encoder); + if (ret) + return ret; + + encoder = new_connector_state->best_encoder; + ret = drm_atomic_add_encoder_bridges(state, encoder); + if (ret) + return ret; + } + ret = mode_valid(state); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index c2cf0c90fa26..37400607e9b7 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/mutex.h> +#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_encoder.h> @@ -89,6 +90,74 @@ void drm_bridge_remove(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_remove); +static struct drm_bridge_state * +drm_atomic_default_bridge_duplicate_state(struct drm_bridge *bridge) +{ + struct drm_bridge_state *new; + + if (WARN_ON(!bridge->base.state)) + return NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (new) + __drm_atomic_helper_bridge_duplicate_state(bridge, new); + + return new; +} + +static struct drm_private_state * +drm_bridge_atomic_duplicate_priv_state(struct drm_private_obj *obj) +{ + struct drm_bridge *bridge = drm_priv_to_bridge(obj); + struct drm_bridge_state *state; + + if (bridge->funcs->atomic_duplicate_state) + state = bridge->funcs->atomic_duplicate_state(bridge); + else + state = drm_atomic_default_bridge_duplicate_state(bridge); + + return state ? &state->base : NULL; +} + +static void +drm_atomic_default_bridge_destroy_state(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + /* Just a simple kfree() for now */ + kfree(state); +} + +static void +drm_bridge_atomic_destroy_priv_state(struct drm_private_obj *obj, + struct drm_private_state *s) +{ + struct drm_bridge_state *state = drm_priv_to_bridge_state(s); + struct drm_bridge *bridge = drm_priv_to_bridge(obj); + + if (bridge->funcs->atomic_destroy_state) + bridge->funcs->atomic_destroy_state(bridge, state); + else + drm_atomic_default_bridge_destroy_state(bridge, state); +} + +static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = { + .atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state, + .atomic_destroy_state = drm_bridge_atomic_destroy_priv_state, +}; + +static struct drm_bridge_state * +drm_atomic_default_bridge_reset(struct drm_bridge *bridge) +{ + struct drm_bridge_state *bridge_state; + + bridge_state = kzalloc(sizeof(*bridge_state), GFP_KERNEL); + if (!bridge_state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_bridge_reset(bridge, bridge_state); + return bridge_state; +} + /** * drm_bridge_attach - attach the bridge to an encoder's chain * @@ -114,6 +183,7 @@ EXPORT_SYMBOL(drm_bridge_remove); int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, struct drm_bridge *previous) { + struct drm_bridge_state *state; int ret; if (!encoder || !bridge) @@ -135,15 +205,35 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, if (bridge->funcs->attach) { ret = bridge->funcs->attach(bridge); - if (ret < 0) { - list_del(&bridge->chain_node); - bridge->dev = NULL; - bridge->encoder = NULL; - return ret; - } + if (ret < 0) + goto err_reset_bridge; + } + + if (bridge->funcs->atomic_reset) + state = bridge->funcs->atomic_reset(bridge); + else + state = drm_atomic_default_bridge_reset(bridge); + + if (IS_ERR(state)) { + ret = PTR_ERR(state); + goto err_detach_bridge; } + drm_atomic_private_obj_init(bridge->dev, &bridge->base, + &state->base, + &drm_bridge_priv_state_funcs); + return 0; + +err_detach_bridge: + if (bridge->funcs->detach) + bridge->funcs->detach(bridge); + +err_reset_bridge: + bridge->dev = NULL; + bridge->encoder = NULL; + list_del(&bridge->chain_node); + return ret; } EXPORT_SYMBOL(drm_bridge_attach); @@ -155,6 +245,8 @@ void drm_bridge_detach(struct drm_bridge *bridge) if (WARN_ON(!bridge->dev)) return; + drm_atomic_private_obj_fini(&bridge->base); + if (bridge->funcs->detach) bridge->funcs->detach(bridge); @@ -409,10 +501,19 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->atomic_disable) - iter->funcs->atomic_disable(iter, old_state); - else if (iter->funcs->disable) + if (iter->funcs->atomic_disable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + iter); + if (WARN_ON(!old_bridge_state)) + return; + + iter->funcs->atomic_disable(iter, old_bridge_state); + } else if (iter->funcs->disable) { iter->funcs->disable(iter); + } if (iter == bridge) break; @@ -443,10 +544,20 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_post_disable) - bridge->funcs->atomic_post_disable(bridge, old_state); - else if (bridge->funcs->post_disable) + if (bridge->funcs->atomic_post_disable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_post_disable(bridge, + old_bridge_state); + } else if (bridge->funcs->post_disable) { bridge->funcs->post_disable(bridge); + } } } EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); @@ -475,10 +586,19 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->atomic_pre_enable) - iter->funcs->atomic_pre_enable(iter, old_state); - else if (iter->funcs->pre_enable) + if (iter->funcs->atomic_pre_enable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + iter); + if (WARN_ON(!old_bridge_state)) + return; + + iter->funcs->atomic_pre_enable(iter, old_bridge_state); + } else if (iter->funcs->pre_enable) { iter->funcs->pre_enable(iter); + } if (iter == bridge) break; @@ -508,14 +628,385 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, encoder = bridge->encoder; list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_enable) - bridge->funcs->atomic_enable(bridge, old_state); - else if (bridge->funcs->enable) + if (bridge->funcs->atomic_enable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_enable(bridge, old_bridge_state); + } else if (bridge->funcs->enable) { bridge->funcs->enable(bridge); + } } } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable); +static int drm_atomic_bridge_check(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + if (bridge->funcs->atomic_check) { + struct drm_bridge_state *bridge_state; + int ret; + + bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + bridge); + if (WARN_ON(!bridge_state)) + return -EINVAL; + + ret = bridge->funcs->atomic_check(bridge, bridge_state, + crtc_state, conn_state); + if (ret) + return ret; + } else if (bridge->funcs->mode_fixup) { + if (!bridge->funcs->mode_fixup(bridge, &crtc_state->mode, + &crtc_state->adjusted_mode)) + return -EINVAL; + } + + return 0; +} + +/** + * drm_atomic_helper_bridge_propagate_bus_fmt() - Propagate output format to + * the input end of a bridge + * @bridge: bridge control structure + * @bridge_state: new bridge state + * @crtc_state: new CRTC state + * @conn_state: new connector state + * @output_fmt: tested output bus format + * @num_input_fmts: will contain the size of the returned array + * + * This helper is a pluggable implementation of the + * &drm_bridge_funcs.atomic_get_input_bus_fmts operation for bridges that don't + * modify the bus configuration between their input and their output. It + * returns an array of input formats with a single element set to @output_fmt. + * + * RETURNS: + * a valid format array of size @num_input_fmts, or NULL if the allocation + * failed + */ +u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + + input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) { + *num_input_fmts = 0; + return NULL; + } + + *num_input_fmts = 1; + input_fmts[0] = output_fmt; + return input_fmts; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt); + +static int select_bus_fmt_recursive(struct drm_bridge *first_bridge, + struct drm_bridge *cur_bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 out_bus_fmt) +{ + struct drm_bridge_state *cur_state; + unsigned int num_in_bus_fmts, i; + struct drm_bridge *prev_bridge; + u32 *in_bus_fmts; + int ret; + + prev_bridge = drm_bridge_get_prev_bridge(cur_bridge); + cur_state = drm_atomic_get_new_bridge_state(crtc_state->state, + cur_bridge); + if (WARN_ON(!cur_state)) + return -EINVAL; + + /* + * If bus format negotiation is not supported by this bridge, let's + * pass MEDIA_BUS_FMT_FIXED to the previous bridge in the chain and + * hope that it can handle this situation gracefully (by providing + * appropriate default values). + */ + if (!cur_bridge->funcs->atomic_get_input_bus_fmts) { + if (cur_bridge != first_bridge) { + ret = select_bus_fmt_recursive(first_bridge, + prev_bridge, crtc_state, + conn_state, + MEDIA_BUS_FMT_FIXED); + if (ret) + return ret; + } + + cur_state->input_bus_cfg.format = MEDIA_BUS_FMT_FIXED; + cur_state->output_bus_cfg.format = out_bus_fmt; + return 0; + } + + in_bus_fmts = cur_bridge->funcs->atomic_get_input_bus_fmts(cur_bridge, + cur_state, + crtc_state, + conn_state, + out_bus_fmt, + &num_in_bus_fmts); + if (!num_in_bus_fmts) + return -ENOTSUPP; + else if (!in_bus_fmts) + return -ENOMEM; + + if (first_bridge == cur_bridge) { + cur_state->input_bus_cfg.format = in_bus_fmts[0]; + cur_state->output_bus_cfg.format = out_bus_fmt; + kfree(in_bus_fmts); + return 0; + } + + for (i = 0; i < num_in_bus_fmts; i++) { + ret = select_bus_fmt_recursive(first_bridge, prev_bridge, + crtc_state, conn_state, + in_bus_fmts[i]); + if (ret != -ENOTSUPP) + break; + } + + if (!ret) { + cur_state->input_bus_cfg.format = in_bus_fmts[i]; + cur_state->output_bus_cfg.format = out_bus_fmt; + } + + kfree(in_bus_fmts); + return ret; +} + +/* + * This function is called by &drm_atomic_bridge_chain_check() just before + * calling &drm_bridge_funcs.atomic_check() on all elements of the chain. + * It performs bus format negotiation between bridge elements. The negotiation + * happens in reverse order, starting from the last element in the chain up to + * @bridge. + * + * Negotiation starts by retrieving supported output bus formats on the last + * bridge element and testing them one by one. The test is recursive, meaning + * that for each tested output format, the whole chain will be walked backward, + * and each element will have to choose an input bus format that can be + * transcoded to the requested output format. When a bridge element does not + * support transcoding into a specific output format -ENOTSUPP is returned and + * the next bridge element will have to try a different format. If none of the + * combinations worked, -ENOTSUPP is returned and the atomic modeset will fail. + * + * This implementation is relying on + * &drm_bridge_funcs.atomic_get_output_bus_fmts() and + * &drm_bridge_funcs.atomic_get_input_bus_fmts() to gather supported + * input/output formats. + * + * When &drm_bridge_funcs.atomic_get_output_bus_fmts() is not implemented by + * the last element of the chain, &drm_atomic_bridge_chain_select_bus_fmts() + * tries a single format: &drm_connector.display_info.bus_formats[0] if + * available, MEDIA_BUS_FMT_FIXED otherwise. + * + * When &drm_bridge_funcs.atomic_get_input_bus_fmts() is not implemented, + * &drm_atomic_bridge_chain_select_bus_fmts() skips the negotiation on the + * bridge element that lacks this hook and asks the previous element in the + * chain to try MEDIA_BUS_FMT_FIXED. It's up to bridge drivers to decide what + * to do in that case (fail if they want to enforce bus format negotiation, or + * provide a reasonable default if they need to support pipelines where not + * all elements support bus format negotiation). + */ +static int +drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_connector *conn = conn_state->connector; + struct drm_encoder *encoder = bridge->encoder; + struct drm_bridge_state *last_bridge_state; + unsigned int i, num_out_bus_fmts; + struct drm_bridge *last_bridge; + u32 *out_bus_fmts; + int ret = 0; + + last_bridge = list_last_entry(&encoder->bridge_chain, + struct drm_bridge, chain_node); + last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, + last_bridge); + if (WARN_ON(!last_bridge_state)) + return -EINVAL; + + if (last_bridge->funcs->atomic_get_output_bus_fmts) { + const struct drm_bridge_funcs *funcs = last_bridge->funcs; + + out_bus_fmts = funcs->atomic_get_output_bus_fmts(last_bridge, + last_bridge_state, + crtc_state, + conn_state, + &num_out_bus_fmts); + if (!num_out_bus_fmts) + return -ENOTSUPP; + else if (!out_bus_fmts) + return -ENOMEM; + } else { + num_out_bus_fmts = 1; + out_bus_fmts = kmalloc(sizeof(*out_bus_fmts), GFP_KERNEL); + if (!out_bus_fmts) + return -ENOMEM; + + if (conn->display_info.num_bus_formats && + conn->display_info.bus_formats) + out_bus_fmts[0] = conn->display_info.bus_formats[0]; + else + out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED; + } + + for (i = 0; i < num_out_bus_fmts; i++) { + ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state, + conn_state, out_bus_fmts[i]); + if (ret != -ENOTSUPP) + break; + } + + kfree(out_bus_fmts); + + return ret; +} + +static void +drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge, + struct drm_connector *conn, + struct drm_atomic_state *state) +{ + struct drm_bridge_state *bridge_state, *next_bridge_state; + struct drm_bridge *next_bridge; + u32 output_flags; + + bridge_state = drm_atomic_get_new_bridge_state(state, bridge); + next_bridge = drm_bridge_get_next_bridge(bridge); + + /* + * Let's try to apply the most common case here, that is, propagate + * display_info flags for the last bridge, and propagate the input + * flags of the next bridge element to the output end of the current + * bridge when the bridge is not the last one. + * There are exceptions to this rule, like when signal inversion is + * happening at the board level, but that's something drivers can deal + * with from their &drm_bridge_funcs.atomic_check() implementation by + * simply overriding the flags value we've set here. + */ + if (!next_bridge) { + output_flags = conn->display_info.bus_flags; + } else { + next_bridge_state = drm_atomic_get_new_bridge_state(state, + next_bridge); + output_flags = next_bridge_state->input_bus_cfg.flags; + } + + bridge_state->output_bus_cfg.flags = output_flags; + + /* + * Propage the output flags to the input end of the bridge. Again, it's + * not necessarily what all bridges want, but that's what most of them + * do, and by doing that by default we avoid forcing drivers to + * duplicate the "dummy propagation" logic. + */ + bridge_state->input_bus_cfg.flags = output_flags; +} + +/** + * drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain + * @bridge: bridge control structure + * @crtc_state: new CRTC state + * @conn_state: new connector state + * + * First trigger a bus format negotiation before calling + * &drm_bridge_funcs.atomic_check() (falls back on + * &drm_bridge_funcs.mode_fixup()) op for all the bridges in the encoder chain, + * starting from the last bridge to the first. These are called before calling + * &drm_encoder_helper_funcs.atomic_check() + * + * RETURNS: + * 0 on success, a negative error code on failure + */ +int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_connector *conn = conn_state->connector; + struct drm_encoder *encoder = bridge->encoder; + struct drm_bridge *iter; + int ret; + + ret = drm_atomic_bridge_chain_select_bus_fmts(bridge, crtc_state, + conn_state); + if (ret) + return ret; + + list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { + int ret; + + /* + * Bus flags are propagated by default. If a bridge needs to + * tweak the input bus flags for any reason, it should happen + * in its &drm_bridge_funcs.atomic_check() implementation such + * that preceding bridges in the chain can propagate the new + * bus flags. + */ + drm_atomic_bridge_propagate_bus_flags(iter, conn, + crtc_state->state); + + ret = drm_atomic_bridge_check(iter, crtc_state, conn_state); + if (ret) + return ret; + + if (iter == bridge) + break; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_bridge_chain_check); + +/** + * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its + * default + * @bridge: the bridge this state is refers to + * @state: bridge state to initialize + * + * Initialize the bridge state to default values. This is meant to be* called + * by the bridge &drm_plane_funcs.reset hook for bridges that subclass the + * bridge state. + */ +void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + memset(state, 0, sizeof(*state)); + state->bridge = bridge; +} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset); + +/** + * __drm_atomic_helper_bridge_duplicate_state() - Copy atomic bridge state + * @bridge: bridge object + * @state: atomic bridge state + * + * Copies atomic state from a bridge's current state and resets inferred values. + * This is useful for drivers that subclass the bridge state. + */ +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + __drm_atomic_helper_private_obj_duplicate_state(&bridge->base, + &state->base); + state->bridge = bridge; +} +EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state); + #ifdef CONFIG_OF /** * of_drm_find_bridge - find the bridge corresponding to the device node in diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 666cb4c22bb9..a4863326061a 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -2,6 +2,7 @@ #include <drm/drm_debugfs.h> #include <drm/drm_device.h> +#include <drm/drm_drv.h> #include <drm/drm_file.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_ttm_helper.h> @@ -92,14 +93,18 @@ static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo, } static int drm_gem_vram_init(struct drm_device *dev, - struct ttm_bo_device *bdev, struct drm_gem_vram_object *gbo, - size_t size, unsigned long pg_align, - bool interruptible) + size_t size, unsigned long pg_align) { + struct drm_vram_mm *vmm = dev->vram_mm; + struct ttm_bo_device *bdev; int ret; size_t acc_size; + if (WARN_ONCE(!vmm, "VRAM MM not initialized")) + return -EINVAL; + bdev = &vmm->bdev; + gbo->bo.base.funcs = &drm_gem_vram_object_funcs; ret = drm_gem_object_init(dev, &gbo->bo.base, size); @@ -112,7 +117,7 @@ static int drm_gem_vram_init(struct drm_device *dev, drm_gem_vram_placement(gbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); ret = ttm_bo_init(bdev, &gbo->bo, size, ttm_bo_type_device, - &gbo->placement, pg_align, interruptible, acc_size, + &gbo->placement, pg_align, false, acc_size, NULL, NULL, ttm_buffer_object_destroy); if (ret) goto err_drm_gem_object_release; @@ -127,29 +132,33 @@ err_drm_gem_object_release: /** * drm_gem_vram_create() - Creates a VRAM-backed GEM object * @dev: the DRM device - * @bdev: the TTM BO device backing the object * @size: the buffer size in bytes * @pg_align: the buffer's alignment in multiples of the page size - * @interruptible: sleep interruptible if waiting for memory * * Returns: * A new instance of &struct drm_gem_vram_object on success, or * an ERR_PTR()-encoded error code otherwise. */ struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, - struct ttm_bo_device *bdev, size_t size, - unsigned long pg_align, - bool interruptible) + unsigned long pg_align) { struct drm_gem_vram_object *gbo; int ret; - gbo = kzalloc(sizeof(*gbo), GFP_KERNEL); - if (!gbo) - return ERR_PTR(-ENOMEM); + if (dev->driver->gem_create_object) { + struct drm_gem_object *gem = + dev->driver->gem_create_object(dev, size); + if (!gem) + return ERR_PTR(-ENOMEM); + gbo = drm_gem_vram_of_gem(gem); + } else { + gbo = kzalloc(sizeof(*gbo), GFP_KERNEL); + if (!gbo) + return ERR_PTR(-ENOMEM); + } - ret = drm_gem_vram_init(dev, bdev, gbo, size, pg_align, interruptible); + ret = drm_gem_vram_init(dev, gbo, size, pg_align); if (ret < 0) goto err_kfree; @@ -483,9 +492,8 @@ EXPORT_SYMBOL(drm_gem_vram_vunmap); Helper for implementing &struct drm_driver.dumb_create * @file: the DRM file * @dev: the DRM device - * @bdev: the TTM BO device managing the buffer object * @pg_align: the buffer's alignment in multiples of the page size - * @interruptible: sleep interruptible if waiting for memory + * @pitch_align: the scanline's alignment in powers of 2 * @args: the arguments as provided to \ &struct drm_driver.dumb_create * @@ -500,9 +508,8 @@ EXPORT_SYMBOL(drm_gem_vram_vunmap); */ int drm_gem_vram_fill_create_dumb(struct drm_file *file, struct drm_device *dev, - struct ttm_bo_device *bdev, unsigned long pg_align, - bool interruptible, + unsigned long pitch_align, struct drm_mode_create_dumb *args) { size_t pitch, size; @@ -510,14 +517,19 @@ int drm_gem_vram_fill_create_dumb(struct drm_file *file, int ret; u32 handle; - pitch = args->width * ((args->bpp + 7) / 8); + pitch = args->width * DIV_ROUND_UP(args->bpp, 8); + if (pitch_align) { + if (WARN_ON_ONCE(!is_power_of_2(pitch_align))) + return -EINVAL; + pitch = ALIGN(pitch, pitch_align); + } size = pitch * args->height; size = roundup(size, PAGE_SIZE); if (!size) return -EINVAL; - gbo = drm_gem_vram_create(dev, bdev, size, pg_align, interruptible); + gbo = drm_gem_vram_create(dev, size, pg_align); if (IS_ERR(gbo)) return PTR_ERR(gbo); @@ -612,8 +624,7 @@ int drm_gem_vram_driver_dumb_create(struct drm_file *file, if (WARN_ONCE(!dev->vram_mm, "VRAM MM not initialized")) return -EINVAL; - return drm_gem_vram_fill_create_dumb(file, dev, &dev->vram_mm->bdev, 0, - false, args); + return drm_gem_vram_fill_create_dumb(file, dev, 0, 0, args); } EXPORT_SYMBOL(drm_gem_vram_driver_dumb_create); diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index e34058c721be..16bff1be4b8a 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -367,9 +367,9 @@ static void mipi_dbi_blank(struct mipi_dbi_dev *dbidev) memset(dbidev->tx_buf, 0, len); mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, 0, 0, - (width >> 8) & 0xFF, (width - 1) & 0xFF); + ((width - 1) >> 8) & 0xFF, (width - 1) & 0xFF); mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, 0, 0, - (height >> 8) & 0xFF, (height - 1) & 0xFF); + ((height - 1) >> 8) & 0xFF, (height - 1) & 0xFF); mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, (u8 *)dbidev->tx_buf, len); diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile index 0c2d4296bccd..f99132715597 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_fbdev.o hibmc_ttm.o +hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_ttm.o obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index 6527a97f68a3..7fa7d4933f60 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -99,14 +99,12 @@ static void hibmc_plane_atomic_update(struct drm_plane *plane, s64 gpu_addr = 0; unsigned int line_l; struct hibmc_drm_private *priv = plane->dev->dev_private; - struct hibmc_framebuffer *hibmc_fb; struct drm_gem_vram_object *gbo; if (!state->fb) return; - hibmc_fb = to_hibmc_framebuffer(state->fb); - gbo = drm_gem_vram_of_gem(hibmc_fb->obj); + gbo = drm_gem_vram_of_gem(state->fb->obj[0]); gpu_addr = drm_gem_vram_offset(gbo); if (WARN_ON_ONCE(gpu_addr < 0)) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 2fd4ca91a62d..11d1b0761c9a 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -17,6 +17,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_gem_vram_helper.h> #include <drm/drm_irq.h> #include <drm/drm_print.h> @@ -54,6 +55,7 @@ static struct drm_driver hibmc_driver = { .desc = "hibmc drm driver", .major = 1, .minor = 0, + .debugfs_init = drm_vram_mm_debugfs_init, .dumb_create = hibmc_dumb_create, .dumb_map_offset = drm_gem_vram_driver_dumb_mmap_offset, .gem_prime_mmap = drm_gem_prime_mmap, @@ -247,8 +249,6 @@ static int hibmc_unload(struct drm_device *dev) { struct hibmc_drm_private *priv = dev->dev_private; - hibmc_fbdev_fini(priv); - drm_atomic_helper_shutdown(dev); if (dev->irq_enabled) @@ -307,7 +307,7 @@ static int hibmc_load(struct drm_device *dev) /* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev); - ret = hibmc_fbdev_init(priv); + ret = drm_fbdev_generic_setup(dev, 16); if (ret) { DRM_ERROR("failed to initialize fbdev: %d\n", ret); goto err; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index e58ecd7edcf8..50a0c1f9d211 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -18,18 +18,6 @@ #include <drm/drm_framebuffer.h> struct drm_device; -struct drm_gem_object; - -struct hibmc_framebuffer { - struct drm_framebuffer fb; - struct drm_gem_object *obj; -}; - -struct hibmc_fbdev { - struct drm_fb_helper helper; /* must be first */ - struct hibmc_framebuffer *fb; - int size; -}; struct hibmc_drm_private { /* hw */ @@ -42,13 +30,8 @@ struct hibmc_drm_private { /* drm */ struct drm_device *dev; bool mode_config_initialized; - - /* fbdev */ - struct hibmc_fbdev *fbdev; }; -#define to_hibmc_framebuffer(x) container_of(x, struct hibmc_framebuffer, fb) - void hibmc_set_power_mode(struct hibmc_drm_private *priv, unsigned int power_mode); void hibmc_set_current_gate(struct hibmc_drm_private *priv, @@ -56,15 +39,6 @@ void hibmc_set_current_gate(struct hibmc_drm_private *priv, int hibmc_de_init(struct hibmc_drm_private *priv); int hibmc_vdac_init(struct hibmc_drm_private *priv); -int hibmc_fbdev_init(struct hibmc_drm_private *priv); -void hibmc_fbdev_fini(struct hibmc_drm_private *priv); - -int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel, - struct drm_gem_object **obj); -struct hibmc_framebuffer * -hibmc_framebuffer_init(struct drm_device *dev, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj); int hibmc_mm_init(struct hibmc_drm_private *hibmc); void hibmc_mm_fini(struct hibmc_drm_private *hibmc); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c deleted file mode 100644 index 1d15560ccb6e..000000000000 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Hisilicon Hibmc SoC drm driver - * - * Based on the bochs drm driver. - * - * Copyright (c) 2016 Huawei Limited. - * - * Author: - * Rongrong Zou <zourongrong@huawei.com> - * Rongrong Zou <zourongrong@gmail.com> - * Jianhua Li <lijianhua@huawei.com> - */ - -#include <drm/drm_crtc.h> -#include <drm/drm_fb_helper.h> -#include <drm/drm_fourcc.h> -#include <drm/drm_gem_vram_helper.h> -#include <drm/drm_probe_helper.h> - -#include "hibmc_drm_drv.h" - -static int hibmcfb_create_object( - struct hibmc_drm_private *priv, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object **gobj_p) -{ - struct drm_gem_object *gobj; - struct drm_device *dev = priv->dev; - u32 size; - int ret = 0; - - size = mode_cmd->pitches[0] * mode_cmd->height; - ret = hibmc_gem_create(dev, size, true, &gobj); - if (ret) - return ret; - - *gobj_p = gobj; - return ret; -} - -static const struct fb_ops hibmc_drm_fb_ops = { - .owner = THIS_MODULE, - .fb_check_var = drm_fb_helper_check_var, - .fb_set_par = drm_fb_helper_set_par, - .fb_fillrect = drm_fb_helper_sys_fillrect, - .fb_copyarea = drm_fb_helper_sys_copyarea, - .fb_imageblit = drm_fb_helper_sys_imageblit, - .fb_pan_display = drm_fb_helper_pan_display, - .fb_blank = drm_fb_helper_blank, - .fb_setcmap = drm_fb_helper_setcmap, -}; - -static int hibmc_drm_fb_create(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct hibmc_fbdev *hi_fbdev = - container_of(helper, struct hibmc_fbdev, helper); - struct hibmc_drm_private *priv = helper->dev->dev_private; - struct fb_info *info; - struct drm_mode_fb_cmd2 mode_cmd; - struct drm_gem_object *gobj = NULL; - int ret = 0; - size_t size; - unsigned int bytes_per_pixel; - struct drm_gem_vram_object *gbo = NULL; - void *base; - - DRM_DEBUG_DRIVER("surface width(%d), height(%d) and bpp(%d)\n", - sizes->surface_width, sizes->surface_height, - sizes->surface_bpp); - - bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); - - mode_cmd.width = sizes->surface_width; - mode_cmd.height = sizes->surface_height; - mode_cmd.pitches[0] = mode_cmd.width * bytes_per_pixel; - mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, - sizes->surface_depth); - - size = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height); - - ret = hibmcfb_create_object(priv, &mode_cmd, &gobj); - if (ret) { - DRM_ERROR("failed to create fbcon backing object: %d\n", ret); - return -ENOMEM; - } - - gbo = drm_gem_vram_of_gem(gobj); - - ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM); - if (ret) { - DRM_ERROR("failed to pin fbcon: %d\n", ret); - goto out_unref_gem; - } - - base = drm_gem_vram_kmap(gbo, true, NULL); - if (IS_ERR(base)) { - ret = PTR_ERR(base); - DRM_ERROR("failed to kmap fbcon: %d\n", ret); - goto out_unpin_bo; - } - - info = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - DRM_ERROR("failed to allocate fbi: %d\n", ret); - goto out_release_fbi; - } - - hi_fbdev->fb = hibmc_framebuffer_init(priv->dev, &mode_cmd, gobj); - if (IS_ERR(hi_fbdev->fb)) { - ret = PTR_ERR(hi_fbdev->fb); - hi_fbdev->fb = NULL; - DRM_ERROR("failed to initialize framebuffer: %d\n", ret); - goto out_release_fbi; - } - - priv->fbdev->size = size; - hi_fbdev->helper.fb = &hi_fbdev->fb->fb; - - info->fbops = &hibmc_drm_fb_ops; - - drm_fb_helper_fill_info(info, &priv->fbdev->helper, sizes); - - info->screen_base = base; - info->screen_size = size; - - info->fix.smem_start = gbo->bo.mem.bus.offset + gbo->bo.mem.bus.base; - info->fix.smem_len = size; - return 0; - -out_release_fbi: - drm_gem_vram_kunmap(gbo); -out_unpin_bo: - drm_gem_vram_unpin(gbo); -out_unref_gem: - drm_gem_object_put_unlocked(gobj); - - return ret; -} - -static void hibmc_fbdev_destroy(struct hibmc_fbdev *fbdev) -{ - struct hibmc_framebuffer *gfb = fbdev->fb; - struct drm_fb_helper *fbh = &fbdev->helper; - - drm_fb_helper_unregister_fbi(fbh); - - drm_fb_helper_fini(fbh); - - if (gfb) - drm_framebuffer_put(&gfb->fb); -} - -static const struct drm_fb_helper_funcs hibmc_fbdev_helper_funcs = { - .fb_probe = hibmc_drm_fb_create, -}; - -int hibmc_fbdev_init(struct hibmc_drm_private *priv) -{ - int ret; - struct fb_var_screeninfo *var; - struct fb_fix_screeninfo *fix; - struct hibmc_fbdev *hifbdev; - - hifbdev = devm_kzalloc(priv->dev->dev, sizeof(*hifbdev), GFP_KERNEL); - if (!hifbdev) { - DRM_ERROR("failed to allocate hibmc_fbdev\n"); - return -ENOMEM; - } - - priv->fbdev = hifbdev; - drm_fb_helper_prepare(priv->dev, &hifbdev->helper, - &hibmc_fbdev_helper_funcs); - - /* Now just one crtc and one channel */ - ret = drm_fb_helper_init(priv->dev, &hifbdev->helper, 1); - if (ret) { - DRM_ERROR("failed to initialize fb helper: %d\n", ret); - return ret; - } - - ret = drm_fb_helper_single_add_all_connectors(&hifbdev->helper); - if (ret) { - DRM_ERROR("failed to add all connectors: %d\n", ret); - goto fini; - } - - ret = drm_fb_helper_initial_config(&hifbdev->helper, 16); - if (ret) { - DRM_ERROR("failed to setup initial conn config: %d\n", ret); - goto fini; - } - - var = &hifbdev->helper.fbdev->var; - fix = &hifbdev->helper.fbdev->fix; - - DRM_DEBUG_DRIVER("Member of info->var is :\n" - "xres=%d\n" - "yres=%d\n" - "xres_virtual=%d\n" - "yres_virtual=%d\n" - "xoffset=%d\n" - "yoffset=%d\n" - "bits_per_pixel=%d\n" - "...\n", var->xres, var->yres, var->xres_virtual, - var->yres_virtual, var->xoffset, var->yoffset, - var->bits_per_pixel); - DRM_DEBUG_DRIVER("Member of info->fix is :\n" - "smem_start=%lx\n" - "smem_len=%d\n" - "type=%d\n" - "type_aux=%d\n" - "visual=%d\n" - "xpanstep=%d\n" - "ypanstep=%d\n" - "ywrapstep=%d\n" - "line_length=%d\n" - "accel=%d\n" - "capabilities=%d\n" - "...\n", fix->smem_start, fix->smem_len, fix->type, - fix->type_aux, fix->visual, fix->xpanstep, - fix->ypanstep, fix->ywrapstep, fix->line_length, - fix->accel, fix->capabilities); - - return 0; - -fini: - drm_fb_helper_fini(&hifbdev->helper); - return ret; -} - -void hibmc_fbdev_fini(struct hibmc_drm_private *priv) -{ - if (!priv->fbdev) - return; - - hibmc_fbdev_destroy(priv->fbdev); - priv->fbdev = NULL; -} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c index 21b684eab5c9..50b988fdd5cc 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c @@ -15,6 +15,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_gem.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_vram_helper.h> #include <drm/drm_print.h> @@ -46,125 +47,14 @@ void hibmc_mm_fini(struct hibmc_drm_private *hibmc) drm_vram_helper_release_mm(hibmc->dev); } -int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel, - struct drm_gem_object **obj) -{ - struct drm_gem_vram_object *gbo; - int ret; - - *obj = NULL; - - size = roundup(size, PAGE_SIZE); - if (size == 0) - return -EINVAL; - - gbo = drm_gem_vram_create(dev, &dev->vram_mm->bdev, size, 0, false); - if (IS_ERR(gbo)) { - ret = PTR_ERR(gbo); - if (ret != -ERESTARTSYS) - DRM_ERROR("failed to allocate GEM object: %d\n", ret); - return ret; - } - *obj = &gbo->bo.base; - return 0; -} - int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { - struct drm_gem_object *gobj; - u32 handle; - int ret; - - args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 16); - args->size = args->pitch * args->height; - - ret = hibmc_gem_create(dev, args->size, false, - &gobj); - if (ret) { - DRM_ERROR("failed to create GEM object: %d\n", ret); - return ret; - } - - ret = drm_gem_handle_create(file, gobj, &handle); - drm_gem_object_put_unlocked(gobj); - if (ret) { - DRM_ERROR("failed to unreference GEM object: %d\n", ret); - return ret; - } - - args->handle = handle; - return 0; -} - -static void hibmc_user_framebuffer_destroy(struct drm_framebuffer *fb) -{ - struct hibmc_framebuffer *hibmc_fb = to_hibmc_framebuffer(fb); - - drm_gem_object_put_unlocked(hibmc_fb->obj); - drm_framebuffer_cleanup(fb); - kfree(hibmc_fb); -} - -static const struct drm_framebuffer_funcs hibmc_fb_funcs = { - .destroy = hibmc_user_framebuffer_destroy, -}; - -struct hibmc_framebuffer * -hibmc_framebuffer_init(struct drm_device *dev, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj) -{ - struct hibmc_framebuffer *hibmc_fb; - int ret; - - hibmc_fb = kzalloc(sizeof(*hibmc_fb), GFP_KERNEL); - if (!hibmc_fb) { - DRM_ERROR("failed to allocate hibmc_fb\n"); - return ERR_PTR(-ENOMEM); - } - - drm_helper_mode_fill_fb_struct(dev, &hibmc_fb->fb, mode_cmd); - hibmc_fb->obj = obj; - ret = drm_framebuffer_init(dev, &hibmc_fb->fb, &hibmc_fb_funcs); - if (ret) { - DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); - kfree(hibmc_fb); - return ERR_PTR(ret); - } - - return hibmc_fb; -} - -static struct drm_framebuffer * -hibmc_user_framebuffer_create(struct drm_device *dev, - struct drm_file *filp, - const struct drm_mode_fb_cmd2 *mode_cmd) -{ - struct drm_gem_object *obj; - struct hibmc_framebuffer *hibmc_fb; - - DRM_DEBUG_DRIVER("%dx%d, format %c%c%c%c\n", - mode_cmd->width, mode_cmd->height, - (mode_cmd->pixel_format) & 0xff, - (mode_cmd->pixel_format >> 8) & 0xff, - (mode_cmd->pixel_format >> 16) & 0xff, - (mode_cmd->pixel_format >> 24) & 0xff); - - obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]); - if (!obj) - return ERR_PTR(-ENOENT); - - hibmc_fb = hibmc_framebuffer_init(dev, mode_cmd, obj); - if (IS_ERR(hibmc_fb)) { - drm_gem_object_put_unlocked(obj); - return ERR_PTR((long)hibmc_fb); - } - return &hibmc_fb->fb; + return drm_gem_vram_fill_create_dumb(file, dev, 0, 16, args); } const struct drm_mode_config_funcs hibmc_mode_funcs = { .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, - .fb_create = hibmc_user_framebuffer_create, + .fb_create = drm_gem_fb_create, }; diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c index 8366a0ed37af..e66b6271ff58 100644 --- a/drivers/gpu/drm/meson/meson_crtc.c +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -413,7 +413,7 @@ void meson_crtc_irq(struct meson_drm *priv) MESON_CANVAS_WRAP_NONE, MESON_CANVAS_BLKMODE_LINEAR, MESON_CANVAS_ENDIAN_SWAP64); - }; + } writel_relaxed(priv->viu.vd1_if0_gen_reg, priv->io_base + meson_crtc->viu_offset + diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c index 534c62879796..d5cbc47835bf 100644 --- a/drivers/gpu/drm/meson/meson_plane.c +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -237,7 +237,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane, /* For ARGB, use the pixel's alpha */ priv->viu.osd1_ctrl_stat2 &= ~OSD_REPLACE_EN; break; - }; + } /* Default scaler parameters */ vsc_bot_rcv_num = 0; diff --git a/drivers/gpu/drm/mgag200/mgag200_cursor.c b/drivers/gpu/drm/mgag200/mgag200_cursor.c index 5444cf1573a3..d491edd317ff 100644 --- a/drivers/gpu/drm/mgag200/mgag200_cursor.c +++ b/drivers/gpu/drm/mgag200/mgag200_cursor.c @@ -208,8 +208,7 @@ int mgag200_cursor_init(struct mga_device *mdev) return -ENOMEM; for (i = 0; i < ncursors; ++i) { - gbo = drm_gem_vram_create(dev, &dev->vram_mm->bdev, - size, 0, false); + gbo = drm_gem_vram_create(dev, size, 0); if (IS_ERR(gbo)) { ret = PTR_ERR(gbo); goto err_drm_gem_vram_put; diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 9f4f5f071add..7a5bad2f57d7 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -27,6 +27,10 @@ int mgag200_modeset = -1; MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); module_param_named(modeset, mgag200_modeset, int, 0400); +int mgag200_hw_bug_no_startadd = -1; +MODULE_PARM_DESC(modeset, "HW does not interpret scanout-buffer start address correctly"); +module_param_named(hw_bug_no_startadd, mgag200_hw_bug_no_startadd, int, 0400); + static struct drm_driver driver; static const struct pci_device_id pciidlist[] = { @@ -97,6 +101,16 @@ DEFINE_DRM_GEM_FOPS(mgag200_driver_fops); static bool mgag200_pin_bo_at_0(const struct mga_device *mdev) { + if (mgag200_hw_bug_no_startadd > 0) { + DRM_WARN_ONCE("Option hw_bug_no_startradd is enabled. Please " + "report the output of 'lspci -vvnn' to " + "<dri-devel@lists.freedesktop.org> if this " + "option is required to make mgag200 work " + "correctly on your system.\n"); + return true; + } else if (!mgag200_hw_bug_no_startadd) { + return false; + } return mdev->flags & MGAG200_FLAG_HW_BUG_NO_STARTADD; } @@ -120,8 +134,7 @@ int mgag200_driver_dumb_create(struct drm_file *file, if (mgag200_pin_bo_at_0(mdev)) pg_align = PFN_UP(mdev->mc.vram_size); - return drm_gem_vram_fill_create_dumb(file, dev, &dev->vram_mm->bdev, - pg_align, false, args); + return drm_gem_vram_fill_create_dumb(file, dev, pg_align, 0, args); } static struct drm_driver driver = { diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 683ff77a3733..41f796b28dd5 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -109,6 +109,17 @@ config DRM_PANEL_KINGDISPLAY_KD097D04 24 bit RGB per pixel. It provides a MIPI DSI interface to the host and has a built-in LED backlight. +config DRM_PANEL_LEADTEK_LTK500HD1829 + tristate "Leadtek LTK500HD1829 panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for Kingdisplay kd097d04 + TFT-LCD modules. The panel has a 1536x2048 resolution and uses + 24 bit RGB per pixel. It provides a MIPI DSI interface to + the host and has a built-in LED backlight. + config DRM_PANEL_SAMSUNG_LD9040 tristate "Samsung LD9040 RGB/SPI panel" depends on OF && SPI @@ -366,4 +377,14 @@ config DRM_PANEL_TRULY_NT35597_WQXGA help Say Y here if you want to enable support for Truly NT35597 WQXGA Dual DSI Video Mode panel + +config DRM_PANEL_XINPENG_XPP055C272 + tristate "Xinpeng XPP055C272 panel driver" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for the Xinpeng + XPP055C272 controller for 720x1280 LCD panels with MIPI/RGB/SPI + system interfaces. endmenu diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 8783476110a3..4dc7acff21b9 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o obj-$(CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04) += panel-kingdisplay-kd097d04.o +obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829) += panel-leadtek-ltk500hd1829.o obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o @@ -39,3 +40,4 @@ obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o +obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c new file mode 100644 index 000000000000..76ecf2de9c44 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 Theobroma Systems Design und Consulting GmbH + * + * base on panel-kingdisplay-kd097d04.c + * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> + +struct ltk500hd1829 { + struct device *dev; + struct drm_panel panel; + struct gpio_desc *reset_gpio; + struct regulator *vcc; + struct regulator *iovcc; + bool prepared; +}; + +struct ltk500hd1829_cmd { + char cmd; + char data; +}; + +/* + * There is no description in the Reference Manual about these commands. + * We received them from the vendor, so just use them as is. + */ +static const struct ltk500hd1829_cmd init_code[] = { + { 0xE0, 0x00 }, + { 0xE1, 0x93 }, + { 0xE2, 0x65 }, + { 0xE3, 0xF8 }, + { 0x80, 0x03 }, + { 0xE0, 0x04 }, + { 0x2D, 0x03 }, + { 0xE0, 0x01 }, + { 0x00, 0x00 }, + { 0x01, 0xB6 }, + { 0x03, 0x00 }, + { 0x04, 0xC5 }, + { 0x17, 0x00 }, + { 0x18, 0xBF }, + { 0x19, 0x01 }, + { 0x1A, 0x00 }, + { 0x1B, 0xBF }, + { 0x1C, 0x01 }, + { 0x1F, 0x7C }, + { 0x20, 0x26 }, + { 0x21, 0x26 }, + { 0x22, 0x4E }, + { 0x37, 0x09 }, + { 0x38, 0x04 }, + { 0x39, 0x08 }, + { 0x3A, 0x1F }, + { 0x3B, 0x1F }, + { 0x3C, 0x78 }, + { 0x3D, 0xFF }, + { 0x3E, 0xFF }, + { 0x3F, 0x00 }, + { 0x40, 0x04 }, + { 0x41, 0xA0 }, + { 0x43, 0x0F }, + { 0x44, 0x0A }, + { 0x45, 0x24 }, + { 0x55, 0x01 }, + { 0x56, 0x01 }, + { 0x57, 0xA5 }, + { 0x58, 0x0A }, + { 0x59, 0x4A }, + { 0x5A, 0x38 }, + { 0x5B, 0x10 }, + { 0x5C, 0x19 }, + { 0x5D, 0x7C }, + { 0x5E, 0x64 }, + { 0x5F, 0x54 }, + { 0x60, 0x48 }, + { 0x61, 0x44 }, + { 0x62, 0x35 }, + { 0x63, 0x3A }, + { 0x64, 0x24 }, + { 0x65, 0x3B }, + { 0x66, 0x39 }, + { 0x67, 0x37 }, + { 0x68, 0x56 }, + { 0x69, 0x41 }, + { 0x6A, 0x47 }, + { 0x6B, 0x2F }, + { 0x6C, 0x23 }, + { 0x6D, 0x13 }, + { 0x6E, 0x02 }, + { 0x6F, 0x08 }, + { 0x70, 0x7C }, + { 0x71, 0x64 }, + { 0x72, 0x54 }, + { 0x73, 0x48 }, + { 0x74, 0x44 }, + { 0x75, 0x35 }, + { 0x76, 0x3A }, + { 0x77, 0x22 }, + { 0x78, 0x3B }, + { 0x79, 0x39 }, + { 0x7A, 0x38 }, + { 0x7B, 0x52 }, + { 0x7C, 0x41 }, + { 0x7D, 0x47 }, + { 0x7E, 0x2F }, + { 0x7F, 0x23 }, + { 0x80, 0x13 }, + { 0x81, 0x02 }, + { 0x82, 0x08 }, + { 0xE0, 0x02 }, + { 0x00, 0x57 }, + { 0x01, 0x77 }, + { 0x02, 0x44 }, + { 0x03, 0x46 }, + { 0x04, 0x48 }, + { 0x05, 0x4A }, + { 0x06, 0x4C }, + { 0x07, 0x4E }, + { 0x08, 0x50 }, + { 0x09, 0x55 }, + { 0x0A, 0x52 }, + { 0x0B, 0x55 }, + { 0x0C, 0x55 }, + { 0x0D, 0x55 }, + { 0x0E, 0x55 }, + { 0x0F, 0x55 }, + { 0x10, 0x55 }, + { 0x11, 0x55 }, + { 0x12, 0x55 }, + { 0x13, 0x40 }, + { 0x14, 0x55 }, + { 0x15, 0x55 }, + { 0x16, 0x57 }, + { 0x17, 0x77 }, + { 0x18, 0x45 }, + { 0x19, 0x47 }, + { 0x1A, 0x49 }, + { 0x1B, 0x4B }, + { 0x1C, 0x4D }, + { 0x1D, 0x4F }, + { 0x1E, 0x51 }, + { 0x1F, 0x55 }, + { 0x20, 0x53 }, + { 0x21, 0x55 }, + { 0x22, 0x55 }, + { 0x23, 0x55 }, + { 0x24, 0x55 }, + { 0x25, 0x55 }, + { 0x26, 0x55 }, + { 0x27, 0x55 }, + { 0x28, 0x55 }, + { 0x29, 0x41 }, + { 0x2A, 0x55 }, + { 0x2B, 0x55 }, + { 0x2C, 0x57 }, + { 0x2D, 0x77 }, + { 0x2E, 0x4F }, + { 0x2F, 0x4D }, + { 0x30, 0x4B }, + { 0x31, 0x49 }, + { 0x32, 0x47 }, + { 0x33, 0x45 }, + { 0x34, 0x41 }, + { 0x35, 0x55 }, + { 0x36, 0x53 }, + { 0x37, 0x55 }, + { 0x38, 0x55 }, + { 0x39, 0x55 }, + { 0x3A, 0x55 }, + { 0x3B, 0x55 }, + { 0x3C, 0x55 }, + { 0x3D, 0x55 }, + { 0x3E, 0x55 }, + { 0x3F, 0x51 }, + { 0x40, 0x55 }, + { 0x41, 0x55 }, + { 0x42, 0x57 }, + { 0x43, 0x77 }, + { 0x44, 0x4E }, + { 0x45, 0x4C }, + { 0x46, 0x4A }, + { 0x47, 0x48 }, + { 0x48, 0x46 }, + { 0x49, 0x44 }, + { 0x4A, 0x40 }, + { 0x4B, 0x55 }, + { 0x4C, 0x52 }, + { 0x4D, 0x55 }, + { 0x4E, 0x55 }, + { 0x4F, 0x55 }, + { 0x50, 0x55 }, + { 0x51, 0x55 }, + { 0x52, 0x55 }, + { 0x53, 0x55 }, + { 0x54, 0x55 }, + { 0x55, 0x50 }, + { 0x56, 0x55 }, + { 0x57, 0x55 }, + { 0x58, 0x40 }, + { 0x59, 0x00 }, + { 0x5A, 0x00 }, + { 0x5B, 0x10 }, + { 0x5C, 0x09 }, + { 0x5D, 0x30 }, + { 0x5E, 0x01 }, + { 0x5F, 0x02 }, + { 0x60, 0x30 }, + { 0x61, 0x03 }, + { 0x62, 0x04 }, + { 0x63, 0x06 }, + { 0x64, 0x6A }, + { 0x65, 0x75 }, + { 0x66, 0x0F }, + { 0x67, 0xB3 }, + { 0x68, 0x0B }, + { 0x69, 0x06 }, + { 0x6A, 0x6A }, + { 0x6B, 0x10 }, + { 0x6C, 0x00 }, + { 0x6D, 0x04 }, + { 0x6E, 0x04 }, + { 0x6F, 0x88 }, + { 0x70, 0x00 }, + { 0x71, 0x00 }, + { 0x72, 0x06 }, + { 0x73, 0x7B }, + { 0x74, 0x00 }, + { 0x75, 0xBC }, + { 0x76, 0x00 }, + { 0x77, 0x05 }, + { 0x78, 0x2E }, + { 0x79, 0x00 }, + { 0x7A, 0x00 }, + { 0x7B, 0x00 }, + { 0x7C, 0x00 }, + { 0x7D, 0x03 }, + { 0x7E, 0x7B }, + { 0xE0, 0x04 }, + { 0x09, 0x10 }, + { 0x2B, 0x2B }, + { 0x2E, 0x44 }, + { 0xE0, 0x00 }, + { 0xE6, 0x02 }, + { 0xE7, 0x02 }, + { 0x35, 0x00 }, +}; + +static inline +struct ltk500hd1829 *panel_to_ltk500hd1829(struct drm_panel *panel) +{ + return container_of(panel, struct ltk500hd1829, panel); +} + +static int ltk500hd1829_unprepare(struct drm_panel *panel) +{ + struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + int ret; + + if (!ctx->prepared) + return 0; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) + DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n", + ret); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n", + ret); + } + + /* 120ms to enter sleep mode */ + msleep(120); + + regulator_disable(ctx->iovcc); + regulator_disable(ctx->vcc); + + ctx->prepared = false; + + return 0; +} + +static int ltk500hd1829_prepare(struct drm_panel *panel) +{ + struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + unsigned int i; + int ret; + + if (ctx->prepared) + return 0; + + ret = regulator_enable(ctx->vcc); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "Failed to enable vci supply: %d\n", ret); + return ret; + } + ret = regulator_enable(ctx->iovcc); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "Failed to enable iovcc supply: %d\n", ret); + goto disable_vcc; + } + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + /* tRW: 10us */ + usleep_range(10, 20); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + + /* tRT: >= 5ms */ + usleep_range(5000, 6000); + + for (i = 0; i < ARRAY_SIZE(init_code); i++) { + ret = mipi_dsi_generic_write(dsi, &init_code[i], + sizeof(struct ltk500hd1829_cmd)); + if (ret < 0) { + DRM_DEV_ERROR(panel->dev, + "failed to write init cmds: %d\n", ret); + goto disable_iovcc; + } + } + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n", + ret); + goto disable_iovcc; + } + + /* 120ms to exit sleep mode */ + msleep(120); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n", + ret); + goto disable_iovcc; + } + + ctx->prepared = true; + + return 0; + +disable_iovcc: + regulator_disable(ctx->iovcc); +disable_vcc: + regulator_disable(ctx->vcc); + return ret; +} + +static const struct drm_display_mode default_mode = { + .hdisplay = 720, + .hsync_start = 720 + 50, + .hsync_end = 720 + 50 + 50, + .htotal = 720 + 50 + 50 + 50, + .vdisplay = 1280, + .vsync_start = 1280 + 30, + .vsync_end = 1280 + 30 + 4, + .vtotal = 1280 + 30 + 4 + 12, + .vrefresh = 60, + .clock = 41600, + .width_mm = 62, + .height_mm = 110, +}; + +static int ltk500hd1829_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel); + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &default_mode); + if (!mode) { + DRM_DEV_ERROR(ctx->dev, "failed to add mode %ux%ux@%u\n", + default_mode.hdisplay, default_mode.vdisplay, + default_mode.vrefresh); + return -ENOMEM; + } + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs ltk500hd1829_funcs = { + .unprepare = ltk500hd1829_unprepare, + .prepare = ltk500hd1829_prepare, + .get_modes = ltk500hd1829_get_modes, +}; + +static int ltk500hd1829_probe(struct mipi_dsi_device *dsi) +{ + struct ltk500hd1829 *ctx; + struct device *dev = &dsi->dev; + int ret; + + ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) { + DRM_DEV_ERROR(dev, "cannot get reset gpio\n"); + return PTR_ERR(ctx->reset_gpio); + } + + ctx->vcc = devm_regulator_get(dev, "vcc"); + if (IS_ERR(ctx->vcc)) { + ret = PTR_ERR(ctx->vcc); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, + "Failed to request vcc regulator: %d\n", + ret); + return ret; + } + + ctx->iovcc = devm_regulator_get(dev, "iovcc"); + if (IS_ERR(ctx->iovcc)) { + ret = PTR_ERR(ctx->iovcc); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, + "Failed to request iovcc regulator: %d\n", + ret); + return ret; + } + + mipi_dsi_set_drvdata(dsi, ctx); + + ctx->dev = dev; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET; + + drm_panel_init(&ctx->panel, &dsi->dev, <k500hd1829_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return ret; + + drm_panel_add(&ctx->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + DRM_DEV_ERROR(dev, "mipi_dsi_attach failed: %d\n", ret); + drm_panel_remove(&ctx->panel); + return ret; + } + + return 0; +} + +static void ltk500hd1829_shutdown(struct mipi_dsi_device *dsi) +{ + struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = drm_panel_unprepare(&ctx->panel); + if (ret < 0) + DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n", + ret); + + ret = drm_panel_disable(&ctx->panel); + if (ret < 0) + DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n", + ret); +} + +static int ltk500hd1829_remove(struct mipi_dsi_device *dsi) +{ + struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ltk500hd1829_shutdown(dsi); + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n", + ret); + + drm_panel_remove(&ctx->panel); + + return 0; +} + +static const struct of_device_id ltk500hd1829_of_match[] = { + { .compatible = "leadtek,ltk500hd1829", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ltk500hd1829_of_match); + +static struct mipi_dsi_driver ltk500hd1829_driver = { + .driver = { + .name = "panel-leadtek-ltk500hd1829", + .of_match_table = ltk500hd1829_of_match, + }, + .probe = ltk500hd1829_probe, + .remove = ltk500hd1829_remove, + .shutdown = ltk500hd1829_shutdown, +}; +module_mipi_dsi_driver(ltk500hd1829_driver); + +MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); +MODULE_DESCRIPTION("Leadtek LTK500HD1829 panel driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c new file mode 100644 index 000000000000..1645aceab597 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver + * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH + * + * based on + * + * Rockteck jh057n00900 5.5" MIPI-DSI panel driver + * Copyright (C) Purism SPC 2019 + */ + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> + +#include <video/display_timing.h> +#include <video/mipi_display.h> + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/media-bus-format.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +/* Manufacturer specific Commands send via DSI */ +#define XPP055C272_CMD_ALL_PIXEL_OFF 0x22 +#define XPP055C272_CMD_ALL_PIXEL_ON 0x23 +#define XPP055C272_CMD_SETDISP 0xb2 +#define XPP055C272_CMD_SETRGBIF 0xb3 +#define XPP055C272_CMD_SETCYC 0xb4 +#define XPP055C272_CMD_SETBGP 0xb5 +#define XPP055C272_CMD_SETVCOM 0xb6 +#define XPP055C272_CMD_SETOTP 0xb7 +#define XPP055C272_CMD_SETPOWER_EXT 0xb8 +#define XPP055C272_CMD_SETEXTC 0xb9 +#define XPP055C272_CMD_SETMIPI 0xbA +#define XPP055C272_CMD_SETVDC 0xbc +#define XPP055C272_CMD_SETPCR 0xbf +#define XPP055C272_CMD_SETSCR 0xc0 +#define XPP055C272_CMD_SETPOWER 0xc1 +#define XPP055C272_CMD_SETECO 0xc6 +#define XPP055C272_CMD_SETPANEL 0xcc +#define XPP055C272_CMD_SETGAMMA 0xe0 +#define XPP055C272_CMD_SETEQ 0xe3 +#define XPP055C272_CMD_SETGIP1 0xe9 +#define XPP055C272_CMD_SETGIP2 0xea + +struct xpp055c272 { + struct device *dev; + struct drm_panel panel; + struct gpio_desc *reset_gpio; + struct regulator *vci; + struct regulator *iovcc; + bool prepared; +}; + +static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel) +{ + return container_of(panel, struct xpp055c272, panel); +} + +#define dsi_generic_write_seq(dsi, cmd, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static int xpp055c272_init_sequence(struct xpp055c272 *ctx) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + struct device *dev = ctx->dev; + + /* + * Init sequence was supplied by the panel vendor without much + * documentation. + */ + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETMIPI, + 0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, + 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01, + 0x00, 0x00, 0x37); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETRGBIF, + 0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00, + 0x00, 0x00); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETSCR, + 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, + 0x00); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEQ, + 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER, + 0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd, + 0x67, 0x77, 0x33, 0x33); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff, + 0xff, 0x01, 0xff); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09); + msleep(20); + + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP1, + 0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12, + 0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18, + 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42, + 0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58, + 0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88, + 0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP2, + 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35, + 0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f, + 0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88, + 0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05, + 0xa0, 0x00, 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGAMMA, + 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36, + 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11, + 0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, + 0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, + 0x11, 0x18); + + msleep(60); + + DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n"); + return 0; +} + +static int xpp055c272_unprepare(struct drm_panel *panel) +{ + struct xpp055c272 *ctx = panel_to_xpp055c272(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + int ret; + + if (!ctx->prepared) + return 0; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) + DRM_DEV_ERROR(ctx->dev, "failed to set display off: %d\n", + ret); + + mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "failed to enter sleep mode: %d\n", + ret); + return ret; + } + + regulator_disable(ctx->iovcc); + regulator_disable(ctx->vci); + + ctx->prepared = false; + + return 0; +} + +static int xpp055c272_prepare(struct drm_panel *panel) +{ + struct xpp055c272 *ctx = panel_to_xpp055c272(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + int ret; + + if (ctx->prepared) + return 0; + + DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n"); + ret = regulator_enable(ctx->vci); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "Failed to enable vci supply: %d\n", ret); + return ret; + } + ret = regulator_enable(ctx->iovcc); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "Failed to enable iovcc supply: %d\n", ret); + goto disable_vci; + } + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + /* T6: 10us */ + usleep_range(10, 20); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + + /* T8: 20ms */ + msleep(20); + + ret = xpp055c272_init_sequence(ctx); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n", + ret); + goto disable_iovcc; + } + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "Failed to exit sleep mode: %d\n", ret); + goto disable_iovcc; + } + + /* T9: 120ms */ + msleep(120); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "Failed to set display on: %d\n", ret); + goto disable_iovcc; + } + + msleep(50); + + ctx->prepared = true; + + return 0; + +disable_iovcc: + regulator_disable(ctx->iovcc); +disable_vci: + regulator_disable(ctx->vci); + return ret; +} + +static const struct drm_display_mode default_mode = { + .hdisplay = 720, + .hsync_start = 720 + 40, + .hsync_end = 720 + 40 + 10, + .htotal = 720 + 40 + 10 + 40, + .vdisplay = 1280, + .vsync_start = 1280 + 22, + .vsync_end = 1280 + 22 + 4, + .vtotal = 1280 + 22 + 4 + 11, + .vrefresh = 60, + .clock = 64000, + .width_mm = 68, + .height_mm = 121, +}; + +static int xpp055c272_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct xpp055c272 *ctx = panel_to_xpp055c272(panel); + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &default_mode); + if (!mode) { + DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n", + default_mode.hdisplay, default_mode.vdisplay, + default_mode.vrefresh); + return -ENOMEM; + } + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs xpp055c272_funcs = { + .unprepare = xpp055c272_unprepare, + .prepare = xpp055c272_prepare, + .get_modes = xpp055c272_get_modes, +}; + +static int xpp055c272_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct xpp055c272 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) { + DRM_DEV_ERROR(dev, "cannot get reset gpio\n"); + return PTR_ERR(ctx->reset_gpio); + } + + ctx->vci = devm_regulator_get(dev, "vci"); + if (IS_ERR(ctx->vci)) { + ret = PTR_ERR(ctx->vci); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, + "Failed to request vci regulator: %d\n", + ret); + return ret; + } + + ctx->iovcc = devm_regulator_get(dev, "iovcc"); + if (IS_ERR(ctx->iovcc)) { + ret = PTR_ERR(ctx->iovcc); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, + "Failed to request iovcc regulator: %d\n", + ret); + return ret; + } + + mipi_dsi_set_drvdata(dsi, ctx); + + ctx->dev = dev; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET; + + drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return ret; + + drm_panel_add(&ctx->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + DRM_DEV_ERROR(dev, "mipi_dsi_attach failed: %d\n", ret); + drm_panel_remove(&ctx->panel); + return ret; + } + + return 0; +} + +static void xpp055c272_shutdown(struct mipi_dsi_device *dsi) +{ + struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = drm_panel_unprepare(&ctx->panel); + if (ret < 0) + DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n", + ret); + + ret = drm_panel_disable(&ctx->panel); + if (ret < 0) + DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n", + ret); +} + +static int xpp055c272_remove(struct mipi_dsi_device *dsi) +{ + struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + xpp055c272_shutdown(dsi); + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n", + ret); + + drm_panel_remove(&ctx->panel); + + return 0; +} + +static const struct of_device_id xpp055c272_of_match[] = { + { .compatible = "xinpeng,xpp055c272" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, xpp055c272_of_match); + +static struct mipi_dsi_driver xpp055c272_driver = { + .driver = { + .name = "panel-xinpeng-xpp055c272", + .of_match_table = xpp055c272_of_match, + }, + .probe = xpp055c272_probe, + .remove = xpp055c272_remove, + .shutdown = xpp055c272_shutdown, +}; +module_mipi_dsi_driver(xpp055c272_driver); + +MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); +MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 8ffa4fbbdeb3..961519ce6634 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -590,8 +590,9 @@ static void __rcar_lvds_atomic_enable(struct drm_bridge *bridge, } static void rcar_lvds_atomic_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_bridge_state *old_bridge_state) { + struct drm_atomic_state *state = old_bridge_state->base.state; struct drm_connector *connector; struct drm_crtc *crtc; @@ -603,7 +604,7 @@ static void rcar_lvds_atomic_enable(struct drm_bridge *bridge, } static void rcar_lvds_atomic_disable(struct drm_bridge *bridge, - struct drm_atomic_state *state) + struct drm_bridge_state *old_bridge_state) { struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); @@ -618,7 +619,8 @@ static void rcar_lvds_atomic_disable(struct drm_bridge *bridge, /* Disable the companion LVDS encoder in dual-link mode. */ if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion) - lvds->companion->funcs->atomic_disable(lvds->companion, state); + lvds->companion->funcs->atomic_disable(lvds->companion, + old_bridge_state); clk_disable_unprepare(lvds->clocks.mod); } diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c index 8673e64723e3..fe203d38664e 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -641,6 +641,9 @@ static int rk3066_hdmi_i2c_write(struct rk3066_hdmi *hdmi, struct i2c_msg *msgs) if (msgs->addr == DDC_ADDR) hdmi->i2c->ddc_addr = msgs->buf[0]; + /* Set edid fifo first address. */ + hdmi_writeb(hdmi, HDMI_EDID_FIFO_ADDR, 0x00); + /* Set edid word address 0x00/0x80. */ hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr); diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index 325811d2e26e..f25a36743cbd 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -10,6 +10,7 @@ #include <linux/component.h> #include <linux/mfd/syscon.h> #include <linux/of_graph.h> +#include <linux/phy/phy.h> #include <linux/pinctrl/devinfo.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -31,6 +32,8 @@ #define DISPLAY_OUTPUT_LVDS 1 #define DISPLAY_OUTPUT_DUAL_LVDS 2 +struct rockchip_lvds; + #define connector_to_lvds(c) \ container_of(c, struct rockchip_lvds, connector) @@ -39,16 +42,12 @@ /** * rockchip_lvds_soc_data - rockchip lvds Soc private data - * @ch1_offset: lvds channel 1 registe offset - * grf_soc_con6: general registe offset for LVDS contrl - * grf_soc_con7: general registe offset for LVDS contrl - * has_vop_sel: to indicate whether need to choose from different VOP. + * @probe: LVDS platform probe function + * @helper_funcs: LVDS connector helper functions */ struct rockchip_lvds_soc_data { - u32 ch1_offset; - int grf_soc_con6; - int grf_soc_con7; - bool has_vop_sel; + int (*probe)(struct platform_device *pdev, struct rockchip_lvds *lvds); + const struct drm_encoder_helper_funcs *helper_funcs; }; struct rockchip_lvds { @@ -56,6 +55,7 @@ struct rockchip_lvds { void __iomem *regs; struct regmap *grf; struct clk *pclk; + struct phy *dphy; const struct rockchip_lvds_soc_data *soc_data; int output; /* rgb lvds or dual lvds output */ int format; /* vesa or jeida format */ @@ -67,15 +67,16 @@ struct rockchip_lvds { struct dev_pin_info *pins; }; -static inline void lvds_writel(struct rockchip_lvds *lvds, u32 offset, u32 val) +static inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset, + u32 val) { writel_relaxed(val, lvds->regs + offset); if (lvds->output == DISPLAY_OUTPUT_LVDS) return; - writel_relaxed(val, lvds->regs + offset + lvds->soc_data->ch1_offset); + writel_relaxed(val, lvds->regs + offset + RK3288_LVDS_CH1_OFFSET); } -static inline int lvds_name_to_format(const char *s) +static inline int rockchip_lvds_name_to_format(const char *s) { if (strncmp(s, "jeida-18", 8) == 0) return LVDS_JEIDA_18; @@ -87,7 +88,7 @@ static inline int lvds_name_to_format(const char *s) return -EINVAL; } -static inline int lvds_name_to_output(const char *s) +static inline int rockchip_lvds_name_to_output(const char *s) { if (strncmp(s, "rgb", 3) == 0) return DISPLAY_OUTPUT_RGB; @@ -99,7 +100,41 @@ static inline int lvds_name_to_output(const char *s) return -EINVAL; } -static int rockchip_lvds_poweron(struct rockchip_lvds *lvds) +static const struct drm_connector_funcs rockchip_lvds_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int rockchip_lvds_connector_get_modes(struct drm_connector *connector) +{ + struct rockchip_lvds *lvds = connector_to_lvds(connector); + struct drm_panel *panel = lvds->panel; + + return drm_panel_get_modes(panel, connector); +} + +static const +struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = { + .get_modes = rockchip_lvds_connector_get_modes, +}; + +static int +rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + + s->output_mode = ROCKCHIP_OUT_MODE_P888; + s->output_type = DRM_MODE_CONNECTOR_LVDS; + + return 0; +} + +static int rk3288_lvds_poweron(struct rockchip_lvds *lvds) { int ret; u32 val; @@ -121,66 +156,73 @@ static int rockchip_lvds_poweron(struct rockchip_lvds *lvds) if (lvds->output == DISPLAY_OUTPUT_RGB) { val |= RK3288_LVDS_CH0_REG0_TTL_EN | RK3288_LVDS_CH0_REG0_LANECK_EN; - lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val); - lvds_writel(lvds, RK3288_LVDS_CH0_REG2, - RK3288_LVDS_PLL_FBDIV_REG2(0x46)); - lvds_writel(lvds, RK3288_LVDS_CH0_REG4, - RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE | - RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE | - RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE | - RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE | - RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE | - RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE); - lvds_writel(lvds, RK3288_LVDS_CH0_REG5, - RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA | - RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA | - RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA | - RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA | - RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA | - RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG2, + RK3288_LVDS_PLL_FBDIV_REG2(0x46)); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG4, + RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE | + RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE | + RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE | + RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE | + RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE | + RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG5, + RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA | + RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA | + RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA | + RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA | + RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA | + RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA); } else { val |= RK3288_LVDS_CH0_REG0_LVDS_EN | RK3288_LVDS_CH0_REG0_LANECK_EN; - lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val); - lvds_writel(lvds, RK3288_LVDS_CH0_REG1, - RK3288_LVDS_CH0_REG1_LANECK_BIAS | - RK3288_LVDS_CH0_REG1_LANE4_BIAS | - RK3288_LVDS_CH0_REG1_LANE3_BIAS | - RK3288_LVDS_CH0_REG1_LANE2_BIAS | - RK3288_LVDS_CH0_REG1_LANE1_BIAS | - RK3288_LVDS_CH0_REG1_LANE0_BIAS); - lvds_writel(lvds, RK3288_LVDS_CH0_REG2, - RK3288_LVDS_CH0_REG2_RESERVE_ON | - RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE | - RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE | - RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE | - RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE | - RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE | - RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE | - RK3288_LVDS_PLL_FBDIV_REG2(0x46)); - lvds_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00); - lvds_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG1, + RK3288_LVDS_CH0_REG1_LANECK_BIAS | + RK3288_LVDS_CH0_REG1_LANE4_BIAS | + RK3288_LVDS_CH0_REG1_LANE3_BIAS | + RK3288_LVDS_CH0_REG1_LANE2_BIAS | + RK3288_LVDS_CH0_REG1_LANE1_BIAS | + RK3288_LVDS_CH0_REG1_LANE0_BIAS); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG2, + RK3288_LVDS_CH0_REG2_RESERVE_ON | + RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE | + RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE | + RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE | + RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE | + RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE | + RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE | + RK3288_LVDS_PLL_FBDIV_REG2(0x46)); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00); } - lvds_writel(lvds, RK3288_LVDS_CH0_REG3, RK3288_LVDS_PLL_FBDIV_REG3(0x46)); - lvds_writel(lvds, RK3288_LVDS_CH0_REGD, RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); - lvds_writel(lvds, RK3288_LVDS_CH0_REG20, RK3288_LVDS_CH0_REG20_LSB); - - lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE); - lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG3, + RK3288_LVDS_PLL_FBDIV_REG3(0x46)); + rk3288_writel(lvds, RK3288_LVDS_CH0_REGD, + RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); + rk3288_writel(lvds, RK3288_LVDS_CH0_REG20, + RK3288_LVDS_CH0_REG20_LSB); + + rk3288_writel(lvds, RK3288_LVDS_CFG_REGC, + RK3288_LVDS_CFG_REGC_PLL_ENABLE); + rk3288_writel(lvds, RK3288_LVDS_CFG_REG21, + RK3288_LVDS_CFG_REG21_TX_ENABLE); return 0; } -static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds) +static void rk3288_lvds_poweroff(struct rockchip_lvds *lvds) { int ret; u32 val; - lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE); - lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE); + rk3288_writel(lvds, RK3288_LVDS_CFG_REG21, + RK3288_LVDS_CFG_REG21_TX_ENABLE); + rk3288_writel(lvds, RK3288_LVDS_CFG_REGC, + RK3288_LVDS_CFG_REGC_PLL_ENABLE); val = LVDS_DUAL | LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN | LVDS_PWRDN; val |= val << 16; - ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val); + ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val); if (ret != 0) DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret); @@ -188,29 +230,8 @@ static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds) clk_disable(lvds->pclk); } -static const struct drm_connector_funcs rockchip_lvds_connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -static int rockchip_lvds_connector_get_modes(struct drm_connector *connector) -{ - struct rockchip_lvds *lvds = connector_to_lvds(connector); - struct drm_panel *panel = lvds->panel; - - return drm_panel_get_modes(panel, connector); -} - -static const -struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = { - .get_modes = rockchip_lvds_connector_get_modes, -}; - -static void rockchip_lvds_grf_config(struct drm_encoder *encoder, - struct drm_display_mode *mode) +static int rk3288_lvds_grf_config(struct drm_encoder *encoder, + struct drm_display_mode *mode) { struct rockchip_lvds *lvds = encoder_to_lvds(encoder); u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0; @@ -234,22 +255,19 @@ static void rockchip_lvds_grf_config(struct drm_encoder *encoder, val |= (pin_dclk << 8) | (pin_hsync << 9); val |= (0xffff << 16); - ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val); - if (ret != 0) { + ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val); + if (ret) DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret); - return; - } + + return ret; } -static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds, - struct drm_encoder *encoder) +static int rk3288_lvds_set_vop_source(struct rockchip_lvds *lvds, + struct drm_encoder *encoder) { u32 val; int ret; - if (!lvds->soc_data->has_vop_sel) - return 0; - ret = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder); if (ret < 0) return ret; @@ -258,56 +276,162 @@ static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds, if (ret) val |= RK3288_LVDS_SOC_CON6_SEL_VOP_LIT; - ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val); + ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON6, val); if (ret < 0) return ret; return 0; } -static int -rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +static void rk3288_lvds_encoder_enable(struct drm_encoder *encoder) { - struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct rockchip_lvds *lvds = encoder_to_lvds(encoder); + struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; + int ret; - s->output_mode = ROCKCHIP_OUT_MODE_P888; - s->output_type = DRM_MODE_CONNECTOR_LVDS; + drm_panel_prepare(lvds->panel); - return 0; + ret = rk3288_lvds_poweron(lvds); + if (ret < 0) { + DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n", ret); + drm_panel_unprepare(lvds->panel); + return; + } + + ret = rk3288_lvds_grf_config(encoder, mode); + if (ret) { + DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n", ret); + drm_panel_unprepare(lvds->panel); + return; + } + + ret = rk3288_lvds_set_vop_source(lvds, encoder); + if (ret) { + DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n", ret); + drm_panel_unprepare(lvds->panel); + return; + } + + drm_panel_enable(lvds->panel); +} + +static void rk3288_lvds_encoder_disable(struct drm_encoder *encoder) +{ + struct rockchip_lvds *lvds = encoder_to_lvds(encoder); + + drm_panel_disable(lvds->panel); + rk3288_lvds_poweroff(lvds); + drm_panel_unprepare(lvds->panel); +} + +static int px30_lvds_poweron(struct rockchip_lvds *lvds) +{ + int ret; + + ret = pm_runtime_get_sync(lvds->dev); + if (ret < 0) { + DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret); + return ret; + } + + /* Enable LVDS mode */ + return regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, + PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1), + PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1)); +} + +static void px30_lvds_poweroff(struct rockchip_lvds *lvds) +{ + regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, + PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1), + PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0)); + + pm_runtime_put(lvds->dev); +} + +static int px30_lvds_grf_config(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct rockchip_lvds *lvds = encoder_to_lvds(encoder); + + if (lvds->output != DISPLAY_OUTPUT_LVDS) { + DRM_DEV_ERROR(lvds->dev, "Unsupported display output %d\n", + lvds->output); + return -EINVAL; + } + + /* Set format */ + return regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, + PX30_LVDS_FORMAT(lvds->format), + PX30_LVDS_FORMAT(lvds->format)); } -static void rockchip_lvds_encoder_enable(struct drm_encoder *encoder) +static int px30_lvds_set_vop_source(struct rockchip_lvds *lvds, + struct drm_encoder *encoder) +{ + int vop; + + vop = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder); + if (vop < 0) + return vop; + + return regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, + PX30_LVDS_VOP_SEL(1), + PX30_LVDS_VOP_SEL(vop)); +} + +static void px30_lvds_encoder_enable(struct drm_encoder *encoder) { struct rockchip_lvds *lvds = encoder_to_lvds(encoder); struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; int ret; drm_panel_prepare(lvds->panel); - ret = rockchip_lvds_poweron(lvds); - if (ret < 0) { - DRM_DEV_ERROR(lvds->dev, "failed to power on lvds: %d\n", ret); + + ret = px30_lvds_poweron(lvds); + if (ret) { + DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n", ret); drm_panel_unprepare(lvds->panel); + return; } - rockchip_lvds_grf_config(encoder, mode); - rockchip_lvds_set_vop_source(lvds, encoder); + + ret = px30_lvds_grf_config(encoder, mode); + if (ret) { + DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n", ret); + drm_panel_unprepare(lvds->panel); + return; + } + + ret = px30_lvds_set_vop_source(lvds, encoder); + if (ret) { + DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n", ret); + drm_panel_unprepare(lvds->panel); + return; + } + drm_panel_enable(lvds->panel); } -static void rockchip_lvds_encoder_disable(struct drm_encoder *encoder) +static void px30_lvds_encoder_disable(struct drm_encoder *encoder) { struct rockchip_lvds *lvds = encoder_to_lvds(encoder); drm_panel_disable(lvds->panel); - rockchip_lvds_poweroff(lvds); + px30_lvds_poweroff(lvds); drm_panel_unprepare(lvds->panel); } static const -struct drm_encoder_helper_funcs rockchip_lvds_encoder_helper_funcs = { - .enable = rockchip_lvds_encoder_enable, - .disable = rockchip_lvds_encoder_disable, +struct drm_encoder_helper_funcs rk3288_lvds_encoder_helper_funcs = { + .enable = rk3288_lvds_encoder_enable, + .disable = rk3288_lvds_encoder_disable, + .atomic_check = rockchip_lvds_encoder_atomic_check, +}; + +static const +struct drm_encoder_helper_funcs px30_lvds_encoder_helper_funcs = { + .enable = px30_lvds_encoder_enable, + .disable = px30_lvds_encoder_disable, .atomic_check = rockchip_lvds_encoder_atomic_check, }; @@ -315,11 +439,88 @@ static const struct drm_encoder_funcs rockchip_lvds_encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static int rk3288_lvds_probe(struct platform_device *pdev, + struct rockchip_lvds *lvds) +{ + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lvds->regs = devm_ioremap_resource(lvds->dev, res); + if (IS_ERR(lvds->regs)) + return PTR_ERR(lvds->regs); + + lvds->pclk = devm_clk_get(lvds->dev, "pclk_lvds"); + if (IS_ERR(lvds->pclk)) { + DRM_DEV_ERROR(lvds->dev, "could not get pclk_lvds\n"); + return PTR_ERR(lvds->pclk); + } + + lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins), + GFP_KERNEL); + if (!lvds->pins) + return -ENOMEM; + + lvds->pins->p = devm_pinctrl_get(lvds->dev); + if (IS_ERR(lvds->pins->p)) { + DRM_DEV_ERROR(lvds->dev, "no pinctrl handle\n"); + devm_kfree(lvds->dev, lvds->pins); + lvds->pins = NULL; + } else { + lvds->pins->default_state = + pinctrl_lookup_state(lvds->pins->p, "lcdc"); + if (IS_ERR(lvds->pins->default_state)) { + DRM_DEV_ERROR(lvds->dev, "no default pinctrl state\n"); + devm_kfree(lvds->dev, lvds->pins); + lvds->pins = NULL; + } + } + + ret = clk_prepare(lvds->pclk); + if (ret < 0) { + DRM_DEV_ERROR(lvds->dev, "failed to prepare pclk_lvds\n"); + return ret; + } + + return 0; +} + +static int px30_lvds_probe(struct platform_device *pdev, + struct rockchip_lvds *lvds) +{ + int ret; + + /* MSB */ + ret = regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, + PX30_LVDS_MSBSEL(1), + PX30_LVDS_MSBSEL(1)); + if (ret) + return ret; + + /* PHY */ + lvds->dphy = devm_phy_get(&pdev->dev, "dphy"); + if (IS_ERR(lvds->dphy)) + return PTR_ERR(lvds->dphy); + + phy_init(lvds->dphy); + if (ret) + return ret; + + phy_set_mode(lvds->dphy, PHY_MODE_LVDS); + if (ret) + return ret; + + return phy_power_on(lvds->dphy); +} + static const struct rockchip_lvds_soc_data rk3288_lvds_data = { - .ch1_offset = 0x100, - .grf_soc_con6 = 0x025c, - .grf_soc_con7 = 0x0260, - .has_vop_sel = true, + .probe = rk3288_lvds_probe, + .helper_funcs = &rk3288_lvds_encoder_helper_funcs, +}; + +static const struct rockchip_lvds_soc_data px30_lvds_data = { + .probe = px30_lvds_probe, + .helper_funcs = &px30_lvds_encoder_helper_funcs, }; static const struct of_device_id rockchip_lvds_dt_ids[] = { @@ -327,6 +528,10 @@ static const struct of_device_id rockchip_lvds_dt_ids[] = { .compatible = "rockchip,rk3288-lvds", .data = &rk3288_lvds_data }, + { + .compatible = "rockchip,px30-lvds", + .data = &px30_lvds_data + }, {} }; MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids); @@ -378,7 +583,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, /* default set it as output rgb */ lvds->output = DISPLAY_OUTPUT_RGB; else - lvds->output = lvds_name_to_output(name); + lvds->output = rockchip_lvds_name_to_output(name); if (lvds->output < 0) { DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name); @@ -390,7 +595,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, /* default set it as format vesa 18 */ lvds->format = LVDS_VESA_18; else - lvds->format = lvds_name_to_format(name); + lvds->format = rockchip_lvds_name_to_format(name); if (lvds->format < 0) { DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name); @@ -410,7 +615,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, goto err_put_remote; } - drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs); + drm_encoder_helper_add(encoder, lvds->soc_data->helper_funcs); if (lvds->panel) { connector = &lvds->connector; @@ -471,8 +676,10 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master, void *data) { struct rockchip_lvds *lvds = dev_get_drvdata(dev); + const struct drm_encoder_helper_funcs *encoder_funcs; - rockchip_lvds_encoder_disable(&lvds->encoder); + encoder_funcs = lvds->soc_data->helper_funcs; + encoder_funcs->disable(&lvds->encoder); if (lvds->panel) drm_panel_detach(lvds->panel); pm_runtime_disable(dev); @@ -490,7 +697,6 @@ static int rockchip_lvds_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rockchip_lvds *lvds; const struct of_device_id *match; - struct resource *res; int ret; if (!dev->of_node) @@ -506,37 +712,6 @@ static int rockchip_lvds_probe(struct platform_device *pdev) return -ENODEV; lvds->soc_data = match->data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - lvds->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(lvds->regs)) - return PTR_ERR(lvds->regs); - - lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds"); - if (IS_ERR(lvds->pclk)) { - DRM_DEV_ERROR(dev, "could not get pclk_lvds\n"); - return PTR_ERR(lvds->pclk); - } - - lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins), - GFP_KERNEL); - if (!lvds->pins) - return -ENOMEM; - - lvds->pins->p = devm_pinctrl_get(lvds->dev); - if (IS_ERR(lvds->pins->p)) { - DRM_DEV_ERROR(dev, "no pinctrl handle\n"); - devm_kfree(lvds->dev, lvds->pins); - lvds->pins = NULL; - } else { - lvds->pins->default_state = - pinctrl_lookup_state(lvds->pins->p, "lcdc"); - if (IS_ERR(lvds->pins->default_state)) { - DRM_DEV_ERROR(dev, "no default pinctrl state\n"); - devm_kfree(lvds->dev, lvds->pins); - lvds->pins = NULL; - } - } - lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); if (IS_ERR(lvds->grf)) { @@ -544,13 +719,14 @@ static int rockchip_lvds_probe(struct platform_device *pdev) return PTR_ERR(lvds->grf); } - dev_set_drvdata(dev, lvds); - - ret = clk_prepare(lvds->pclk); - if (ret < 0) { - DRM_DEV_ERROR(dev, "failed to prepare pclk_lvds\n"); + ret = lvds->soc_data->probe(pdev, lvds); + if (ret) { + DRM_DEV_ERROR(dev, "Platform initialization failed\n"); return ret; } + + dev_set_drvdata(dev, lvds); + ret = component_add(&pdev->dev, &rockchip_lvds_component_ops); if (ret < 0) { DRM_DEV_ERROR(dev, "failed to add component\n"); diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.h b/drivers/gpu/drm/rockchip/rockchip_lvds.h index 029bad8e1a14..4ce967d23813 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.h +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.h @@ -70,7 +70,10 @@ #define RK3288_LVDS_CFG_REG21 0x84 #define RK3288_LVDS_CFG_REG21_TX_ENABLE 0x92 #define RK3288_LVDS_CFG_REG21_TX_DISABLE 0x00 -#define RK3288_LVDS_CH1_OFFSET 0x100 +#define RK3288_LVDS_CH1_OFFSET 0x100 + +#define RK3288_LVDS_GRF_SOC_CON6 0x025C +#define RK3288_LVDS_GRF_SOC_CON7 0x0260 /* fbdiv value is split over 2 registers, with bit8 in reg2 */ #define RK3288_LVDS_PLL_FBDIV_REG2(_fbd) \ @@ -103,4 +106,18 @@ #define LVDS_VESA_18 2 #define LVDS_JEIDA_18 3 +#define HIWORD_UPDATE(v, h, l) ((GENMASK(h, l) << 16) | ((v) << (l))) + +#define PX30_LVDS_GRF_PD_VO_CON0 0x434 +#define PX30_LVDS_TIE_CLKS(val) HIWORD_UPDATE(val, 8, 8) +#define PX30_LVDS_INVERT_CLKS(val) HIWORD_UPDATE(val, 9, 9) +#define PX30_LVDS_INVERT_DCLK(val) HIWORD_UPDATE(val, 5, 5) + +#define PX30_LVDS_GRF_PD_VO_CON1 0x438 +#define PX30_LVDS_FORMAT(val) HIWORD_UPDATE(val, 14, 13) +#define PX30_LVDS_MODE_EN(val) HIWORD_UPDATE(val, 12, 12) +#define PX30_LVDS_MSBSEL(val) HIWORD_UPDATE(val, 11, 11) +#define PX30_LVDS_P2S_EN(val) HIWORD_UPDATE(val, 6, 6) +#define PX30_LVDS_VOP_SEL(val) HIWORD_UPDATE(val, 1, 1) + #endif /* _ROCKCHIP_LVDS_ */ diff --git a/drivers/video/fbdev/68328fb.c b/drivers/video/fbdev/68328fb.c index 491fd5a82993..9811f1bad8d4 100644 --- a/drivers/video/fbdev/68328fb.c +++ b/drivers/video/fbdev/68328fb.c @@ -405,20 +405,8 @@ static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma) int __init mc68x328fb_setup(char *options) { -#if 0 - char *this_opt; -#endif - if (!options || !*options) return 1; -#if 0 - while ((this_opt = strsep(&options, ",")) != NULL) { - if (!*this_opt) - continue; - if (!strncmp(this_opt, "disable", 7)) - mc68x328fb_enable = 0; - } -#endif return 1; } diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index c9235a2f42f8..bb6ae995c2e5 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -536,6 +536,13 @@ static int __init fb_console_setup(char *this_opt) fb_center_logo = true; continue; } + + if (!strncmp(options, "logo-count:", 11)) { + options += 11; + if (*options) + fb_logo_count = simple_strtol(options, &options, 0); + continue; + } } return 1; } diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 0662b61fdb50..d04554959ea7 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -54,7 +54,8 @@ int num_registered_fb __read_mostly; EXPORT_SYMBOL(num_registered_fb); bool fb_center_logo __read_mostly; -EXPORT_SYMBOL(fb_center_logo); + +int fb_logo_count __read_mostly = -1; static struct fb_info *get_fb_info(unsigned int idx) { @@ -620,7 +621,7 @@ int fb_prepare_logo(struct fb_info *info, int rotate) memset(&fb_logo, 0, sizeof(struct logo_data)); if (info->flags & FBINFO_MISC_TILEBLITTING || - info->fbops->owner) + info->fbops->owner || !fb_logo_count) return 0; if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { @@ -686,10 +687,14 @@ int fb_prepare_logo(struct fb_info *info, int rotate) int fb_show_logo(struct fb_info *info, int rotate) { + unsigned int count; int y; - y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, - num_online_cpus()); + if (!fb_logo_count) + return 0; + + count = fb_logo_count < 0 ? num_online_cpus() : fb_logo_count; + y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, count); y = fb_show_extra_logos(info, y, rotate); return y; diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c index 7cacabdd5f12..67ebfe5c9f1d 100644 --- a/drivers/video/fbdev/fsl-diu-fb.c +++ b/drivers/video/fbdev/fsl-diu-fb.c @@ -1287,6 +1287,7 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, dev_warn(info->dev, "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n", MFB_SET_PIXFMT_OLD); + /* fall through */ case MFB_SET_PIXFMT: if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt))) return -EFAULT; @@ -1296,6 +1297,7 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, dev_warn(info->dev, "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n", MFB_GET_PIXFMT_OLD); + /* fall through */ case MFB_GET_PIXFMT: pix_fmt = ad->pix_fmt; if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt))) diff --git a/drivers/video/fbdev/matrox/matroxfb_misc.c b/drivers/video/fbdev/matrox/matroxfb_misc.c index c7aaca12805e..8f159a2ad8d0 100644 --- a/drivers/video/fbdev/matrox/matroxfb_misc.c +++ b/drivers/video/fbdev/matrox/matroxfb_misc.c @@ -673,7 +673,10 @@ static int parse_pins5(struct matrox_fb_info *minfo, if (bd->pins[115] & 4) { minfo->values.reg.mctlwtst_core = minfo->values.reg.mctlwtst; } else { - u_int32_t wtst_xlat[] = { 0, 1, 5, 6, 7, 5, 2, 3 }; + static const u8 wtst_xlat[] = { + 0, 1, 5, 6, 7, 5, 2, 3 + }; + minfo->values.reg.mctlwtst_core = (minfo->values.reg.mctlwtst & ~7) | wtst_xlat[minfo->values.reg.mctlwtst & 7]; } diff --git a/drivers/video/fbdev/mmp/Kconfig b/drivers/video/fbdev/mmp/Kconfig index 9041ffd2cfcf..5c6cc97c96f0 100644 --- a/drivers/video/fbdev/mmp/Kconfig +++ b/drivers/video/fbdev/mmp/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig MMP_DISP tristate "Marvell MMP Display Subsystem support" - depends on CPU_PXA910 || CPU_MMP2 + depends on CPU_PXA910 || CPU_MMP2 || COMPILE_TEST help Marvell Display Subsystem support. diff --git a/drivers/video/fbdev/mmp/fb/Kconfig b/drivers/video/fbdev/mmp/fb/Kconfig index 39944eb23ef8..0ec2e3fb9e17 100644 --- a/drivers/video/fbdev/mmp/fb/Kconfig +++ b/drivers/video/fbdev/mmp/fb/Kconfig @@ -1,6 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -if MMP_DISP - config MMP_FB tristate "fb driver for Marvell MMP Display Subsystem" depends on FB @@ -10,5 +8,3 @@ config MMP_FB default y help fb driver for Marvell MMP Display Subsystem - -endif diff --git a/drivers/video/fbdev/mmp/fb/mmpfb.c b/drivers/video/fbdev/mmp/fb/mmpfb.c index 33db0850086f..01c75c031cb6 100644 --- a/drivers/video/fbdev/mmp/fb/mmpfb.c +++ b/drivers/video/fbdev/mmp/fb/mmpfb.c @@ -522,7 +522,7 @@ static int fb_info_setup(struct fb_info *info, info->var.bits_per_pixel / 8; info->fbops = &mmpfb_ops; info->pseudo_palette = fbi->pseudo_palette; - info->screen_base = fbi->fb_start; + info->screen_buffer = fbi->fb_start; info->screen_size = fbi->fb_size; /* For FB framework: Allocate color map and Register framebuffer*/ diff --git a/drivers/video/fbdev/mmp/hw/Kconfig b/drivers/video/fbdev/mmp/hw/Kconfig index 4d018cf661ec..7ebe125093d5 100644 --- a/drivers/video/fbdev/mmp/hw/Kconfig +++ b/drivers/video/fbdev/mmp/hw/Kconfig @@ -1,9 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only -if MMP_DISP - config MMP_DISP_CONTROLLER bool "mmp display controller hw support" - depends on CPU_PXA910 || CPU_MMP2 + depends on HAVE_CLK && HAS_IOMEM + depends on CPU_PXA910 || CPU_MMP2 || COMPILE_TEST help Marvell MMP display hw controller support this controller is used on Marvell PXA910 and @@ -16,5 +15,3 @@ config MMP_DISP_SPI help Marvell MMP display hw controller spi port support will register as a spi master for panel usage - -endif diff --git a/drivers/video/fbdev/mmp/hw/mmp_ctrl.c b/drivers/video/fbdev/mmp/hw/mmp_ctrl.c index 17174cd7a5bb..b877a606ce26 100644 --- a/drivers/video/fbdev/mmp/hw/mmp_ctrl.c +++ b/drivers/video/fbdev/mmp/hw/mmp_ctrl.c @@ -136,19 +136,26 @@ static void overlay_set_win(struct mmp_overlay *overlay, struct mmp_win *win) mutex_lock(&overlay->access_ok); if (overlay_is_vid(overlay)) { - writel_relaxed(win->pitch[0], ®s->v_pitch_yc); - writel_relaxed(win->pitch[2] << 16 | - win->pitch[1], ®s->v_pitch_uv); - - writel_relaxed((win->ysrc << 16) | win->xsrc, ®s->v_size); - writel_relaxed((win->ydst << 16) | win->xdst, ®s->v_size_z); - writel_relaxed(win->ypos << 16 | win->xpos, ®s->v_start); + writel_relaxed(win->pitch[0], + (void __iomem *)®s->v_pitch_yc); + writel_relaxed(win->pitch[2] << 16 | win->pitch[1], + (void __iomem *)®s->v_pitch_uv); + + writel_relaxed((win->ysrc << 16) | win->xsrc, + (void __iomem *)®s->v_size); + writel_relaxed((win->ydst << 16) | win->xdst, + (void __iomem *)®s->v_size_z); + writel_relaxed(win->ypos << 16 | win->xpos, + (void __iomem *)®s->v_start); } else { - writel_relaxed(win->pitch[0], ®s->g_pitch); - - writel_relaxed((win->ysrc << 16) | win->xsrc, ®s->g_size); - writel_relaxed((win->ydst << 16) | win->xdst, ®s->g_size_z); - writel_relaxed(win->ypos << 16 | win->xpos, ®s->g_start); + writel_relaxed(win->pitch[0], (void __iomem *)®s->g_pitch); + + writel_relaxed((win->ysrc << 16) | win->xsrc, + (void __iomem *)®s->g_size); + writel_relaxed((win->ydst << 16) | win->xdst, + (void __iomem *)®s->g_size_z); + writel_relaxed(win->ypos << 16 | win->xpos, + (void __iomem *)®s->g_start); } dmafetch_set_fmt(overlay); @@ -233,11 +240,11 @@ static int overlay_set_addr(struct mmp_overlay *overlay, struct mmp_addr *addr) memcpy(&overlay->addr, addr, sizeof(struct mmp_addr)); if (overlay_is_vid(overlay)) { - writel_relaxed(addr->phys[0], ®s->v_y0); - writel_relaxed(addr->phys[1], ®s->v_u0); - writel_relaxed(addr->phys[2], ®s->v_v0); + writel_relaxed(addr->phys[0], (void __iomem *)®s->v_y0); + writel_relaxed(addr->phys[1], (void __iomem *)®s->v_u0); + writel_relaxed(addr->phys[2], (void __iomem *)®s->v_v0); } else - writel_relaxed(addr->phys[0], ®s->g_0); + writel_relaxed(addr->phys[0], (void __iomem *)®s->g_0); return overlay->addr.phys[0]; } @@ -268,16 +275,18 @@ static void path_set_mode(struct mmp_path *path, struct mmp_mode *mode) tmp |= dsi_rbswap & CFG_INTFRBSWAP_MASK; writel_relaxed(tmp, ctrl_regs(path) + intf_rbswap_ctrl(path->id)); - writel_relaxed((mode->yres << 16) | mode->xres, ®s->screen_active); + writel_relaxed((mode->yres << 16) | mode->xres, + (void __iomem *)®s->screen_active); writel_relaxed((mode->left_margin << 16) | mode->right_margin, - ®s->screen_h_porch); + (void __iomem *)®s->screen_h_porch); writel_relaxed((mode->upper_margin << 16) | mode->lower_margin, - ®s->screen_v_porch); + (void __iomem *)®s->screen_v_porch); total_x = mode->xres + mode->left_margin + mode->right_margin + mode->hsync_len; total_y = mode->yres + mode->upper_margin + mode->lower_margin + mode->vsync_len; - writel_relaxed((total_y << 16) | total_x, ®s->screen_size); + writel_relaxed((total_y << 16) | total_x, + (void __iomem *)®s->screen_size); /* vsync ctrl */ if (path->output_type == PATH_OUT_DSI) @@ -285,7 +294,7 @@ static void path_set_mode(struct mmp_path *path, struct mmp_mode *mode) else vsync_ctrl = ((mode->xres + mode->right_margin) << 16) | (mode->xres + mode->right_margin); - writel_relaxed(vsync_ctrl, ®s->vsync_ctrl); + writel_relaxed(vsync_ctrl, (void __iomem *)®s->vsync_ctrl); /* set pixclock div */ sclk_src = clk_get_rate(path_to_ctrl(path)->clk); @@ -366,9 +375,9 @@ static void path_set_default(struct mmp_path *path) writel_relaxed(dma_ctrl1, ctrl_regs(path) + dma_ctrl(1, path->id)); /* Configure default register values */ - writel_relaxed(0x00000000, ®s->blank_color); - writel_relaxed(0x00000000, ®s->g_1); - writel_relaxed(0x00000000, ®s->g_start); + writel_relaxed(0x00000000, (void __iomem *)®s->blank_color); + writel_relaxed(0x00000000, (void __iomem *)®s->g_1); + writel_relaxed(0x00000000, (void __iomem *)®s->g_start); /* * 1.enable multiple burst request in DMA AXI diff --git a/drivers/video/fbdev/mmp/hw/mmp_ctrl.h b/drivers/video/fbdev/mmp/hw/mmp_ctrl.h index e9ec45c118fb..335d4983dc52 100644 --- a/drivers/video/fbdev/mmp/hw/mmp_ctrl.h +++ b/drivers/video/fbdev/mmp/hw/mmp_ctrl.h @@ -1393,7 +1393,7 @@ struct mmphw_ctrl { /* platform related, get from config */ const char *name; int irq; - void *reg_base; + void __iomem *reg_base; struct clk *clk; /* sys info */ @@ -1429,7 +1429,7 @@ static inline struct mmphw_ctrl *overlay_to_ctrl(struct mmp_overlay *overlay) return path_to_ctrl(overlay->path); } -static inline void *ctrl_regs(struct mmp_path *path) +static inline void __iomem *ctrl_regs(struct mmp_path *path) { return path_to_ctrl(path)->reg_base; } @@ -1438,11 +1438,11 @@ static inline void *ctrl_regs(struct mmp_path *path) static inline struct lcd_regs *path_regs(struct mmp_path *path) { if (path->id == PATH_PN) - return (struct lcd_regs *)(ctrl_regs(path) + 0xc0); + return (struct lcd_regs __force *)(ctrl_regs(path) + 0xc0); else if (path->id == PATH_TV) - return (struct lcd_regs *)ctrl_regs(path); + return (struct lcd_regs __force *)ctrl_regs(path); else if (path->id == PATH_P2) - return (struct lcd_regs *)(ctrl_regs(path) + 0x200); + return (struct lcd_regs __force *)(ctrl_regs(path) + 0x200); else { dev_err(path->dev, "path id %d invalid\n", path->id); BUG_ON(1); diff --git a/drivers/video/fbdev/mmp/hw/mmp_spi.c b/drivers/video/fbdev/mmp/hw/mmp_spi.c index bbb75de5e441..1911a47769b6 100644 --- a/drivers/video/fbdev/mmp/hw/mmp_spi.c +++ b/drivers/video/fbdev/mmp/hw/mmp_spi.c @@ -31,7 +31,7 @@ static inline int lcd_spi_write(struct spi_device *spi, u32 data) { int timeout = 100000, isr, ret = 0; u32 tmp; - void *reg_base = + void __iomem *reg_base = (void __iomem *) *(void **)spi_master_get_devdata(spi->master); /* clear ISR */ @@ -80,7 +80,7 @@ static inline int lcd_spi_write(struct spi_device *spi, u32 data) static int lcd_spi_setup(struct spi_device *spi) { - void *reg_base = + void __iomem *reg_base = (void __iomem *) *(void **)spi_master_get_devdata(spi->master); u32 tmp; @@ -146,7 +146,7 @@ int lcd_spi_register(struct mmphw_ctrl *ctrl) return -ENOMEM; } p_regbase = spi_master_get_devdata(master); - *p_regbase = ctrl->reg_base; + *p_regbase = (void __force *)ctrl->reg_base; /* set bus num to 5 to avoid conflict with other spi hosts */ master->bus_num = 5; diff --git a/drivers/video/fbdev/ocfb.c b/drivers/video/fbdev/ocfb.c index c8cee2d890dc..bfa4ed421148 100644 --- a/drivers/video/fbdev/ocfb.c +++ b/drivers/video/fbdev/ocfb.c @@ -297,7 +297,6 @@ static int ocfb_probe(struct platform_device *pdev) { int ret = 0; struct ocfb_dev *fbdev; - struct resource *res; int fbsize; fbdev = devm_kzalloc(&pdev->dev, sizeof(*fbdev), GFP_KERNEL); @@ -319,13 +318,7 @@ static int ocfb_probe(struct platform_device *pdev) ocfb_init_var(fbdev); ocfb_init_fix(fbdev); - /* Request I/O resource */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "I/O resource request failed\n"); - return -ENXIO; - } - fbdev->regs = devm_ioremap_resource(&pdev->dev, res); + fbdev->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(fbdev->regs)) return PTR_ERR(fbdev->regs); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c index 376ee5bc3ddc..ce37da85cc45 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c @@ -1635,7 +1635,7 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, { int scale_x = out_width != orig_width; int scale_y = out_height != orig_height; - bool chroma_upscale = plane != OMAP_DSS_WB ? true : false; + bool chroma_upscale = plane != OMAP_DSS_WB; if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) return; @@ -3100,9 +3100,9 @@ static bool _dispc_mgr_pclk_ok(enum omap_channel channel, unsigned long pclk) { if (dss_mgr_is_lcd(channel)) - return pclk <= dispc.feat->max_lcd_pclk ? true : false; + return pclk <= dispc.feat->max_lcd_pclk; else - return pclk <= dispc.feat->max_tv_pclk ? true : false; + return pclk <= dispc.feat->max_tv_pclk; } bool dispc_mgr_timings_ok(enum omap_channel channel, diff --git a/drivers/video/fbdev/omap2/omapfb/vrfb.c b/drivers/video/fbdev/omap2/omapfb/vrfb.c index 819e0bc35b2d..ee0dd4c6a646 100644 --- a/drivers/video/fbdev/omap2/omapfb/vrfb.c +++ b/drivers/video/fbdev/omap2/omapfb/vrfb.c @@ -339,9 +339,7 @@ static int __init vrfb_probe(struct platform_device *pdev) int i; /* first resource is the register res, the rest are vrfb contexts */ - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - vrfb_base = devm_ioremap_resource(&pdev->dev, mem); + vrfb_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(vrfb_base)) return PTR_ERR(vrfb_base); diff --git a/drivers/video/fbdev/pxa168fb.c b/drivers/video/fbdev/pxa168fb.c index c672c3354a2c..362d3dfe8287 100644 --- a/drivers/video/fbdev/pxa168fb.c +++ b/drivers/video/fbdev/pxa168fb.c @@ -766,8 +766,8 @@ failed_free_cmap: failed_free_clk: clk_disable_unprepare(fbi->clk); failed_free_fbmem: - dma_free_coherent(fbi->dev, info->fix.smem_len, - info->screen_base, fbi->fb_start_dma); + dma_free_wc(fbi->dev, info->fix.smem_len, + info->screen_base, fbi->fb_start_dma); failed_free_info: kfree(info); @@ -801,7 +801,7 @@ static int pxa168fb_remove(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); - dma_free_wc(fbi->dev, PAGE_ALIGN(info->fix.smem_len), + dma_free_wc(fbi->dev, info->fix.smem_len, info->screen_base, info->fix.smem_start); clk_disable_unprepare(fbi->clk); diff --git a/drivers/video/fbdev/pxafb.c b/drivers/video/fbdev/pxafb.c index 6f7e9fb63190..00b96a78676e 100644 --- a/drivers/video/fbdev/pxafb.c +++ b/drivers/video/fbdev/pxafb.c @@ -2237,7 +2237,6 @@ static int pxafb_probe(struct platform_device *dev) { struct pxafb_info *fbi; struct pxafb_mach_info *inf, *pdata; - struct resource *r; int i, irq, ret; dev_dbg(&dev->dev, "pxafb_probe\n"); @@ -2303,14 +2302,7 @@ static int pxafb_probe(struct platform_device *dev) fbi->lcd_supply = NULL; } - r = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (r == NULL) { - dev_err(&dev->dev, "no I/O memory resource defined\n"); - ret = -ENODEV; - goto failed; - } - - fbi->mmio_base = devm_ioremap_resource(&dev->dev, r); + fbi->mmio_base = devm_platform_ioremap_resource(dev, 0); if (IS_ERR(fbi->mmio_base)) { dev_err(&dev->dev, "failed to get I/O memory\n"); ret = -EBUSY; diff --git a/drivers/video/fbdev/s3c-fb.c b/drivers/video/fbdev/s3c-fb.c index f4871c430f8d..9dc925054930 100644 --- a/drivers/video/fbdev/s3c-fb.c +++ b/drivers/video/fbdev/s3c-fb.c @@ -1411,8 +1411,7 @@ static int s3c_fb_probe(struct platform_device *pdev) pm_runtime_enable(sfb->dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sfb->regs = devm_ioremap_resource(dev, res); + sfb->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sfb->regs)) { ret = PTR_ERR(sfb->regs); goto err_lcd_clk; diff --git a/drivers/video/fbdev/sa1100fb.c b/drivers/video/fbdev/sa1100fb.c index 4680cc3efb81..5bb653db0cec 100644 --- a/drivers/video/fbdev/sa1100fb.c +++ b/drivers/video/fbdev/sa1100fb.c @@ -1143,7 +1143,6 @@ static struct sa1100fb_info *sa1100fb_init_fbinfo(struct device *dev) static int sa1100fb_probe(struct platform_device *pdev) { struct sa1100fb_info *fbi; - struct resource *res; int ret, irq; if (!dev_get_platdata(&pdev->dev)) { @@ -1159,8 +1158,7 @@ static int sa1100fb_probe(struct platform_device *pdev) if (!fbi) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fbi->base = devm_ioremap_resource(&pdev->dev, res); + fbi->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(fbi->base)) return PTR_ERR(fbi->base); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 951dfb15c27b..ccce65e14917 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -670,6 +670,9 @@ __drm_atomic_get_current_plane_state(struct drm_atomic_state *state, } int __must_check +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, + struct drm_encoder *encoder); +int __must_check drm_atomic_add_affected_connectors(struct drm_atomic_state *state, struct drm_crtc *crtc); int __must_check diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 694e153a7531..46e15526b087 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -25,6 +25,8 @@ #include <linux/list.h> #include <linux/ctype.h> + +#include <drm/drm_atomic.h> #include <drm/drm_encoder.h> #include <drm/drm_mode_object.h> #include <drm/drm_modes.h> @@ -34,6 +36,65 @@ struct drm_bridge_timings; struct drm_panel; /** + * struct drm_bus_cfg - bus configuration + * + * This structure stores the configuration of a physical bus between two + * components in an output pipeline, usually between two bridges, an encoder + * and a bridge, or a bridge and a connector. + * + * The bus configuration is stored in &drm_bridge_state separately for the + * input and output buses, as seen from the point of view of each bridge. The + * bus configuration of a bridge output is usually identical to the + * configuration of the next bridge's input, but may differ if the signals are + * modified between the two bridges, for instance by an inverter on the board. + * The input and output configurations of a bridge may differ if the bridge + * modifies the signals internally, for instance by performing format + * conversion, or modifying signals polarities. + */ +struct drm_bus_cfg { + /** + * @format: format used on this bus (one of the MEDIA_BUS_FMT_* format) + * + * This field should not be directly modified by drivers + * (&drm_atomic_bridge_chain_select_bus_fmts() takes care of the bus + * format negotiation). + */ + u32 format; + + /** + * @flags: DRM_BUS_* flags used on this bus + */ + u32 flags; +}; + +/** + * struct drm_bridge_state - Atomic bridge state object + * @base: inherit from &drm_private_state + * @bridge: the bridge this state refers to + */ +struct drm_bridge_state { + struct drm_private_state base; + + struct drm_bridge *bridge; + + /** + * @input_bus_cfg: input bus configuration + */ + struct drm_bus_cfg input_bus_cfg; + + /** + * @output_bus_cfg: input bus configuration + */ + struct drm_bus_cfg output_bus_cfg; +}; + +static inline struct drm_bridge_state * +drm_priv_to_bridge_state(struct drm_private_state *priv) +{ + return container_of(priv, struct drm_bridge_state, base); +} + +/** * struct drm_bridge_funcs - drm_bridge control functions */ struct drm_bridge_funcs { @@ -109,7 +170,9 @@ struct drm_bridge_funcs { * this function passes all other callbacks must succeed for this * configuration. * - * The @mode_fixup callback is optional. + * The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup() + * is not called when &drm_bridge_funcs.atomic_check() is implemented, + * so only one of them should be provided. * * NOTE: * @@ -263,7 +326,7 @@ struct drm_bridge_funcs { * The @atomic_pre_enable callback is optional. */ void (*atomic_pre_enable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state); /** * @atomic_enable: @@ -288,7 +351,7 @@ struct drm_bridge_funcs { * The @atomic_enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state); /** * @atomic_disable: * @@ -311,7 +374,7 @@ struct drm_bridge_funcs { * The @atomic_disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state); /** * @atomic_post_disable: @@ -337,7 +400,146 @@ struct drm_bridge_funcs { * The @atomic_post_disable callback is optional. */ void (*atomic_post_disable)(struct drm_bridge *bridge, - struct drm_atomic_state *old_state); + struct drm_bridge_state *old_bridge_state); + + /** + * @atomic_duplicate_state: + * + * Duplicate the current bridge state object (which is guaranteed to be + * non-NULL). + * + * The atomic_duplicate_state() is optional. When not implemented the + * core allocates a drm_bridge_state object and calls + * &__drm_atomic_helper_bridge_duplicate_state() to initialize it. + * + * RETURNS: + * A valid drm_bridge_state object or NULL if the allocation fails. + */ + struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge); + + /** + * @atomic_destroy_state: + * + * Destroy a bridge state object previously allocated by + * &drm_bridge_funcs.atomic_duplicate_state(). + * + * The atomic_destroy_state hook is optional. When not implemented the + * core calls kfree() on the state. + */ + void (*atomic_destroy_state)(struct drm_bridge *bridge, + struct drm_bridge_state *state); + + /** + * @atomic_get_output_bus_fmts: + * + * Return the supported bus formats on the output end of a bridge. + * The returned array must be allocated with kmalloc() and will be + * freed by the caller. If the allocation fails, NULL should be + * returned. num_output_fmts must be set to the returned array size. + * Formats listed in the returned array should be listed in decreasing + * preference order (the core will try all formats until it finds one + * that works). + * + * This method is only called on the last element of the bridge chain + * as part of the bus format negotiation process that happens in + * &drm_atomic_bridge_chain_select_bus_fmts(). + * This method is optional. When not implemented, the core will + * fall back to &drm_connector.display_info.bus_formats[0] if + * &drm_connector.display_info.num_bus_formats > 0, + * or to MEDIA_BUS_FMT_FIXED otherwise. + */ + u32 *(*atomic_get_output_bus_fmts)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts); + + /** + * @atomic_get_input_bus_fmts: + * + * Return the supported bus formats on the input end of a bridge for + * a specific output bus format. + * + * The returned array must be allocated with kmalloc() and will be + * freed by the caller. If the allocation fails, NULL should be + * returned. num_output_fmts must be set to the returned array size. + * Formats listed in the returned array should be listed in decreasing + * preference order (the core will try all formats until it finds one + * that works). When the format is not supported NULL should be + * returned and *num_output_fmts should be set to 0. + * + * This method is called on all elements of the bridge chain as part of + * the bus format negotiation process that happens in + * &drm_atomic_bridge_chain_select_bus_fmts(). + * This method is optional. When not implemented, the core will bypass + * bus format negotiation on this element of the bridge without + * failing, and the previous element in the chain will be passed + * MEDIA_BUS_FMT_FIXED as its output bus format. + * + * Bridge drivers that need to support being linked to bridges that are + * not supporting bus format negotiation should handle the + * output_fmt == MEDIA_BUS_FMT_FIXED case appropriately, by selecting a + * sensible default value or extracting this information from somewhere + * else (FW property, &drm_display_mode, &drm_display_info, ...) + * + * Note: Even if input format selection on the first bridge has no + * impact on the negotiation process (bus format negotiation stops once + * we reach the first element of the chain), drivers are expected to + * return accurate input formats as the input format may be used to + * configure the CRTC output appropriately. + */ + u32 *(*atomic_get_input_bus_fmts)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + + /** + * @atomic_check: + * + * This method is responsible for checking bridge state correctness. + * It can also check the state of the surrounding components in chain + * to make sure the whole pipeline can work properly. + * + * &drm_bridge_funcs.atomic_check() hooks are called in reverse + * order (from the last to the first bridge). + * + * This method is optional. &drm_bridge_funcs.mode_fixup() is not + * called when &drm_bridge_funcs.atomic_check() is implemented, so only + * one of them should be provided. + * + * If drivers need to tweak &drm_bridge_state.input_bus_cfg.flags or + * &drm_bridge_state.output_bus_cfg.flags it should should happen in + * this function. By default the &drm_bridge_state.output_bus_cfg.flags + * field is set to the next bridge + * &drm_bridge_state.input_bus_cfg.flags value or + * &drm_connector.display_info.bus_flags if the bridge is the last + * element in the chain. + * + * RETURNS: + * zero if the check passed, a negative error code otherwise. + */ + int (*atomic_check)(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); + + /** + * @atomic_reset: + * + * Reset the bridge to a predefined state (or retrieve its current + * state) and return a &drm_bridge_state object matching this state. + * This function is called at attach time. + * + * The atomic_reset hook is optional. When not implemented the core + * allocates a new state and calls &__drm_atomic_helper_bridge_reset(). + * + * RETURNS: + * A valid drm_bridge_state object in case of success, an ERR_PTR() + * giving the reason of the failure otherwise. + */ + struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge); }; /** @@ -380,6 +582,8 @@ struct drm_bridge_timings { * struct drm_bridge - central DRM bridge control structure */ struct drm_bridge { + /** @base: inherit from &drm_private_object */ + struct drm_private_obj base; /** @dev: DRM device this bridge belongs to */ struct drm_device *dev; /** @encoder: encoder to which this bridge is connected */ @@ -404,6 +608,12 @@ struct drm_bridge { void *driver_private; }; +static inline struct drm_bridge * +drm_priv_to_bridge(struct drm_private_obj *priv) +{ + return container_of(priv, struct drm_bridge, base); +} + void drm_bridge_add(struct drm_bridge *bridge); void drm_bridge_remove(struct drm_bridge *bridge); struct drm_bridge *of_drm_find_bridge(struct device_node *np); @@ -482,6 +692,9 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, void drm_bridge_chain_pre_enable(struct drm_bridge *bridge); void drm_bridge_chain_enable(struct drm_bridge *bridge); +int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, struct drm_atomic_state *state); void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, @@ -491,6 +704,58 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *state); +u32 * +drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts); + +void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, + struct drm_bridge_state *state); +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge, + struct drm_bridge_state *new); + +static inline struct drm_bridge_state * +drm_atomic_get_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge) +{ + struct drm_private_state *obj_state; + + obj_state = drm_atomic_get_private_obj_state(state, &bridge->base); + if (IS_ERR(obj_state)) + return ERR_CAST(obj_state); + + return drm_priv_to_bridge_state(obj_state); +} + +static inline struct drm_bridge_state * +drm_atomic_get_old_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge) +{ + struct drm_private_state *obj_state; + + obj_state = drm_atomic_get_old_private_obj_state(state, &bridge->base); + if (!obj_state) + return NULL; + + return drm_priv_to_bridge_state(obj_state); +} + +static inline struct drm_bridge_state * +drm_atomic_get_new_bridge_state(struct drm_atomic_state *state, + struct drm_bridge *bridge) +{ + struct drm_private_state *obj_state; + + obj_state = drm_atomic_get_new_private_obj_state(state, &bridge->base); + if (!obj_state) + return NULL; + + return drm_priv_to_bridge_state(obj_state); +} + #ifdef CONFIG_DRM_PANEL_BRIDGE struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel); struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 08adaf3695ea..573e9fd109bf 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -93,10 +93,8 @@ static inline struct drm_gem_vram_object *drm_gem_vram_of_gem( } struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, - struct ttm_bo_device *bdev, size_t size, - unsigned long pg_align, - bool interruptible); + unsigned long pg_align); void drm_gem_vram_put(struct drm_gem_vram_object *gbo); u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo); s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); @@ -110,9 +108,8 @@ void drm_gem_vram_vunmap(struct drm_gem_vram_object *gbo, void *vaddr); int drm_gem_vram_fill_create_dumb(struct drm_file *file, struct drm_device *dev, - struct ttm_bo_device *bdev, unsigned long pg_align, - bool interruptible, + unsigned long pitch_align, struct drm_mode_create_dumb *args); /* diff --git a/include/linux/fb.h b/include/linux/fb.h index 6557fabdea62..3b4b2f0c6994 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -625,6 +625,7 @@ extern int fb_new_modelist(struct fb_info *info); extern struct fb_info *registered_fb[FB_MAX]; extern int num_registered_fb; extern bool fb_center_logo; +extern int fb_logo_count; extern struct class *fb_class; #define for_each_registered_fb(i) \ |