drm-misc-next for v5.18:

UAPI Changes:
 
 Cross-subsystem Changes:
 - Improve performance of some fbdev ops, in some cases up to 6x faster.
 
 Core Changes:
 - Some small DP fixes.
 - Find panels in subnodes of OF devices, and add of_get_drm_panel_display_mode
   to retrieve mode.
 - Add drm_object_property_get_default_value and use it for resetting
   zpos in plane state reset, removing the need for individual drivers
   to do it.
 - Same for color encoding and color range props.
 - Update panic handling todo doc.
 - Add todo that format conversion helpers should be sped up similarly to fbdev ops.
 
 Driver Changes:
 - Add panel orientation property to simpledrm for quirked panels.
 - Assorted small fixes to tiny/repaper, nouveau, stm, omap, ssd130x.
 - Add crc support to stm/ltdc.
 - Add MIPI DBI compatible SPI driver
 - Assorted small fixes to tiny panels and bridge drivers.
 - Add AST2600 support to aspeed.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAmIgiiwACgkQ/lWMcqZw
 E8Nw8A//b9QTz/UJoeWo22IuDGJkM/LoUP1WCt9RvfY+caMJ1/Gk7k0KIjq9+Fo9
 +DevKpAfpC8tCMAbzHvB/tRZ67h3ctxk7SLqPdT3blmblWF6zGxJbF8GYDMskvhn
 1FTM9oczOkxhn5+CRw5D5ud0HcSxgqkIrUAzlcIli8wX9hSVwoqlAeRls+0VvORW
 Qp556Ji/x18VqhDU70r9JhevTszhvP7iCtg8I+JBezkKnyFiCx1Ca3Kwhz7NWq3k
 LDEmH0wTAr0cXYO7Of0w68Kii7uTo3OBv+tZ74/LFOpUdndjhltbvMB0qNNms/wr
 3h2j0hvg7gcGRpfzJKXgjXKaevEnVxQH4NDUrLLElTOPydnick4Jqai4GYEpliek
 cPvE1IrPn9WHBQwIIdiiAcIarRmdka14+K8fJEXUWIIn5QQeOiUxTau3V5O8xCYB
 K8xphNM1YG6IkHs4NVtGmVHi/MC/0jR7PxAjsbwlcHD/GqMrvqoUru6NA6DowQii
 YbWjH5kmzy1K8s3SA9CJXsa42cVk4sh2WqbLhcBq+0lDotcMiu8cIyG8HO3VOAl5
 Nuy1Lud8tS6BD/eotsw29HQsYHA/pmZpFlCm1ABkqfLFsqjwPz0DBHROsaB42g5C
 ma2K+ebRXdAFGAN0YCtGu/rlkGrSODWiagZSlaLWcu1izUoJSx4=
 =TVQP
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2022-03-03' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for v5.18:

UAPI Changes:

Cross-subsystem Changes:
- Improve performance of some fbdev ops, in some cases up to 6x faster.

Core Changes:
- Some small DP fixes.
- Find panels in subnodes of OF devices, and add of_get_drm_panel_display_mode
  to retrieve mode.
- Add drm_object_property_get_default_value and use it for resetting
  zpos in plane state reset, removing the need for individual drivers
  to do it.
- Same for color encoding and color range props.
- Update panic handling todo doc.
- Add todo that format conversion helpers should be sped up similarly to fbdev ops.

Driver Changes:
- Add panel orientation property to simpledrm for quirked panels.
- Assorted small fixes to tiny/repaper, nouveau, stm, omap, ssd130x.
- Add crc support to stm/ltdc.
- Add MIPI DBI compatible SPI driver
- Assorted small fixes to tiny panels and bridge drivers.
- Add AST2600 support to aspeed.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/48fabd78-ade9-f80b-c724-13726c7be69e@linux.intel.com
This commit is contained in:
Dave Airlie 2022-03-04 13:41:52 +10:00
commit c9e9ce0b6f
50 changed files with 1051 additions and 572 deletions

View File

@ -0,0 +1,126 @@
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/panel-mipi-dbi-spi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MIPI DBI SPI Panel
maintainers:
- Noralf Trønnes <noralf@tronnes.org>
description: |
This binding is for display panels using a MIPI DBI compatible controller
in SPI mode.
The MIPI Alliance Standard for Display Bus Interface defines the electrical
and logical interfaces for display controllers historically used in mobile
phones. The standard defines 4 display architecture types and this binding is
for type 1 which has full frame memory. There are 3 interface types in the
standard and type C is the serial interface.
The standard defines the following interface signals for type C:
- Power:
- Vdd: Power supply for display module
- Vddi: Logic level supply for interface signals
Combined into one in this binding called: power-supply
- Interface:
- CSx: Chip select
- SCL: Serial clock
- Dout: Serial out
- Din: Serial in
- SDA: Bidrectional in/out
- D/CX: Data/command selection, high=data, low=command
Called dc-gpios in this binding.
- RESX: Reset when low
Called reset-gpios in this binding.
The type C interface has 3 options:
- Option 1: 9-bit mode and D/CX as the 9th bit
| Command | the next command or following data |
|<0><D7><D6><D5><D4><D3><D2><D1><D0>|<D/CX><D7><D6><D5><D4><D3><D2><D1><D0>|
- Option 2: 16-bit mode and D/CX as a 9th bit
| Command or data |
|<X><X><X><X><X><X><X><D/CX><D7><D6><D5><D4><D3><D2><D1><D0>|
- Option 3: 8-bit mode and D/CX as a separate interface line
| Command or data |
|<D7><D6><D5><D4><D3><D2><D1><D0>|
The panel resolution is specified using the panel-timing node properties
hactive (width) and vactive (height). The other mandatory panel-timing
properties should be set to zero except clock-frequency which can be
optionally set to inform about the actual pixel clock frequency.
If the panel is wired to the controller at an offset specify this using
hback-porch (x-offset) and vback-porch (y-offset).
allOf:
- $ref: panel-common.yaml#
- $ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
items:
- enum:
- sainsmart18
- const: panel-mipi-dbi-spi
write-only:
type: boolean
description:
Controller is not readable (ie. Din (MISO on the SPI interface) is not
wired up).
dc-gpios:
maxItems: 1
description: |
Controller data/command selection (D/CX) in 4-line SPI mode.
If not set, the controller is in 3-line SPI mode.
required:
- compatible
- reg
- panel-timing
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
display@0{
compatible = "sainsmart18", "panel-mipi-dbi-spi";
reg = <0>;
spi-max-frequency = <40000000>;
dc-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio 25 GPIO_ACTIVE_HIGH>;
write-only;
backlight = <&backlight>;
width-mm = <35>;
height-mm = <28>;
panel-timing {
hactive = <160>;
vactive = <128>;
hback-porch = <0>;
vback-porch = <0>;
clock-frequency = <0>;
hfront-porch = <0>;
hsync-len = <0>;
vfront-porch = <0>;
vsync-len = <0>;
};
};
};
...

View File

@ -241,6 +241,28 @@ Contact: Thomas Zimmermann <tzimmermann@suse.de>, Daniel Vetter
Level: Advanced
Benchmark and optimize blitting and format-conversion function
--------------------------------------------------------------
Drawing to dispay memory quickly is crucial for many applications'
performance.
On at least x86-64, sys_imageblit() is significantly slower than
cfb_imageblit(), even though both use the same blitting algorithm and
the latter is written for I/O memory. It turns out that cfb_imageblit()
uses movl instructions, while sys_imageblit apparently does not. This
seems to be a problem with gcc's optimizer. DRM's format-conversion
helpers might be subject to similar issues.
Benchmark and optimize fbdev's sys_() helpers and DRM's format-conversion
helpers. In cases that can be further optimized, maybe implement a different
algorithm. For micro-optimizations, use movl/movq instructions explicitly.
That might possibly require architecture-specific helpers (e.g., storel()
storeq()).
Contact: Thomas Zimmermann <tzimmermann@suse.de>
Level: Intermediate
drm_framebuffer_funcs and drm_mode_config_funcs.fb_create cleanup
-----------------------------------------------------------------
@ -475,8 +497,12 @@ This is a really varied tasks with lots of little bits and pieces:
achieved by using an IPI to the local processor.
* There's a massive confusion of different panic handlers. DRM fbdev emulation
helpers have one, but on top of that the fbcon code itself also has one. We
need to make sure that they stop fighting over each another.
helpers had their own (long removed), but on top of that the fbcon code itself
also has one. We need to make sure that they stop fighting over each other.
This is worked around by checking ``oops_in_progress`` at various entry points
into the DRM fbdev emulation helpers. A much cleaner approach here would be to
switch fbcon to the `threaded printk support
<https://lwn.net/Articles/800946/>`_.
* ``drm_can_sleep()`` is a mess. It hides real bugs in normal operations and
isn't a full solution for panic paths. We need to make sure that it only
@ -488,16 +514,15 @@ This is a really varied tasks with lots of little bits and pieces:
even spinlocks (because NMI and hardirq can panic too). We need to either
make sure to not call such paths, or trylock everything. Really tricky.
* For the above locking troubles reasons it's pretty much impossible to
attempt a synchronous modeset from panic handlers. The only thing we could
try to achive is an atomic ``set_base`` of the primary plane, and hope that
it shows up. Everything else probably needs to be delayed to some worker or
something else which happens later on. Otherwise it just kills the box
harder, prevent the panic from going out on e.g. netconsole.
* A clean solution would be an entirely separate panic output support in KMS,
bypassing the current fbcon support. See `[PATCH v2 0/3] drm: Add panic handling
<https://lore.kernel.org/dri-devel/20190311174218.51899-1-noralf@tronnes.org/>`_.
* There's also proposal for a simplied DRM console instead of the full-blown
fbcon and DRM fbdev emulation. Any kind of panic handling tricks should
obviously work for both console, in case we ever get kmslog merged.
* Encoding the actual oops and preceding dmesg in a QR might help with the
dread "important stuff scrolled away" problem. See `[RFC][PATCH] Oops messages
transfer using QR codes
<https://lore.kernel.org/lkml/1446217392-11981-1-git-send-email-alexandru.murtaza@intel.com/>`_
for some example code that could be reused.
Contact: Daniel Vetter

View File

@ -6112,6 +6112,14 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/display/multi-inno,mi0283qt.txt
F: drivers/gpu/drm/tiny/mi0283qt.c
DRM DRIVER FOR MIPI DBI compatible panels
M: Noralf Trønnes <noralf@tronnes.org>
S: Maintained
W: https://github.com/notro/panel-mipi-dbi/wiki
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/display/panel/panel-mipi-dbi-spi.yaml
F: drivers/gpu/drm/tiny/panel-mipi-dbi.c
DRM DRIVER FOR MSM ADRENO GPU
M: Rob Clark <robdclark@gmail.com>
M: Sean Paul <sean@poorly.run>

View File

@ -12,6 +12,7 @@ struct aspeed_gfx {
struct regmap *scu;
u32 dac_reg;
u32 int_clr_reg;
u32 vga_scratch_reg;
u32 throd_val;
u32 scan_line_max;

View File

@ -61,6 +61,7 @@
struct aspeed_gfx_config {
u32 dac_reg; /* DAC register in SCU */
u32 int_clear_reg; /* Interrupt clear register */
u32 vga_scratch_reg; /* VGA scratch register in SCU */
u32 throd_val; /* Default Threshold Seting */
u32 scan_line_max; /* Max memory size of one scan line */
@ -68,6 +69,7 @@ struct aspeed_gfx_config {
static const struct aspeed_gfx_config ast2400_config = {
.dac_reg = 0x2c,
.int_clear_reg = 0x60,
.vga_scratch_reg = 0x50,
.throd_val = CRT_THROD_LOW(0x1e) | CRT_THROD_HIGH(0x12),
.scan_line_max = 64,
@ -75,14 +77,24 @@ static const struct aspeed_gfx_config ast2400_config = {
static const struct aspeed_gfx_config ast2500_config = {
.dac_reg = 0x2c,
.int_clear_reg = 0x60,
.vga_scratch_reg = 0x50,
.throd_val = CRT_THROD_LOW(0x24) | CRT_THROD_HIGH(0x3c),
.scan_line_max = 128,
};
static const struct aspeed_gfx_config ast2600_config = {
.dac_reg = 0xc0,
.int_clear_reg = 0x68,
.vga_scratch_reg = 0x50,
.throd_val = CRT_THROD_LOW(0x50) | CRT_THROD_HIGH(0x70),
.scan_line_max = 128,
};
static const struct of_device_id aspeed_gfx_match[] = {
{ .compatible = "aspeed,ast2400-gfx", .data = &ast2400_config },
{ .compatible = "aspeed,ast2500-gfx", .data = &ast2500_config },
{ .compatible = "aspeed,ast2600-gfx", .data = &ast2600_config },
{ },
};
MODULE_DEVICE_TABLE(of, aspeed_gfx_match);
@ -120,7 +132,7 @@ static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data)
if (reg & CRT_CTRL_VERTICAL_INTR_STS) {
drm_crtc_handle_vblank(&priv->pipe.crtc);
writel(reg, priv->base + CRT_CTRL1);
writel(reg, priv->base + priv->int_clr_reg);
return IRQ_HANDLED;
}
@ -148,6 +160,7 @@ static int aspeed_gfx_load(struct drm_device *drm)
config = match->data;
priv->dac_reg = config->dac_reg;
priv->int_clr_reg = config->int_clear_reg;
priv->vga_scratch_reg = config->vga_scratch_reg;
priv->throd_val = config->throd_val;
priv->scan_line_max = config->scan_line_max;

View File

@ -253,6 +253,8 @@ static int anx7625_aux_trans(struct anx7625_data *ctx, u8 op, u32 address,
addrm = (address >> 8) & 0xFF;
addrh = (address >> 16) & 0xFF;
if (!is_write)
op &= ~DP_AUX_I2C_MOT;
cmd = DPCD_CMD(len, op);
/* Set command and length */
@ -2736,8 +2738,8 @@ static int anx7625_i2c_remove(struct i2c_client *client)
if (platform->hdcp_workqueue) {
cancel_delayed_work(&platform->hdcp_work);
flush_workqueue(platform->workqueue);
destroy_workqueue(platform->workqueue);
flush_workqueue(platform->hdcp_workqueue);
destroy_workqueue(platform->hdcp_workqueue);
}
if (!platform->pdata.low_power_mode)

View File

@ -1284,6 +1284,7 @@ static const struct of_device_id cdns_dsi_of_match[] = {
{ .compatible = "cdns,dsi" },
{ },
};
MODULE_DEVICE_TABLE(of, cdns_dsi_of_match);
static struct platform_driver cdns_dsi_platform_driver = {
.probe = cdns_dsi_drm_probe,

View File

@ -191,7 +191,6 @@ static const struct drm_bridge_funcs chipone_bridge_funcs = {
static int chipone_parse_dt(struct chipone *icn)
{
struct device *dev = icn->dev;
struct drm_panel *panel;
int ret;
icn->vdd1 = devm_regulator_get_optional(dev, "vdd1");
@ -227,11 +226,7 @@ static int chipone_parse_dt(struct chipone *icn)
return PTR_ERR(icn->enable_gpio);
}
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
if (ret)
return ret;
icn->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
icn->panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
if (IS_ERR(icn->panel_bridge))
return PTR_ERR(icn->panel_bridge);

View File

@ -289,7 +289,7 @@
#define WORD_LENGTH_20BIT 2
#define WORD_LENGTH_24BIT 3
#define DEBUGFS_DIR_NAME "it6505-debugfs"
#define READ_BUFFER_SIZE 200
#define READ_BUFFER_SIZE 400
/* Vendor option */
#define HDCP_DESIRED 1
@ -3074,7 +3074,7 @@ static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf,
struct it6505 *it6505 = file->private_data;
struct drm_display_mode *vid = &it6505->video_info;
u8 read_buf[READ_BUFFER_SIZE];
u8 *str = read_buf, *end = read_buf + PAGE_SIZE;
u8 *str = read_buf, *end = read_buf + READ_BUFFER_SIZE;
ssize_t ret, count;
if (!it6505)

View File

@ -332,17 +332,13 @@ static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
static int nwl_dsi_init_interrupts(struct nwl_dsi *dsi)
{
u32 irq_enable;
nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, 0xffffffff);
nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, 0x7);
irq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK |
NWL_DSI_RX_PKT_HDR_RCVD_MASK |
NWL_DSI_TX_FIFO_OVFLW_MASK |
NWL_DSI_HS_TX_TIMEOUT_MASK);
u32 irq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK |
NWL_DSI_RX_PKT_HDR_RCVD_MASK |
NWL_DSI_TX_FIFO_OVFLW_MASK |
NWL_DSI_HS_TX_TIMEOUT_MASK);
nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, irq_enable);
nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, 0x7);
return nwl_dsi_clear_error(dsi);
}

View File

@ -208,16 +208,6 @@ bool drm_dp_128b132b_link_training_failed(const u8 link_status[DP_LINK_STATUS_SI
}
EXPORT_SYMBOL(drm_dp_128b132b_link_training_failed);
u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE],
unsigned int lane)
{
unsigned int offset = DP_ADJUST_REQUEST_POST_CURSOR2;
u8 value = dp_link_status(link_status, offset);
return (value >> (lane << 1)) & 0x3;
}
EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor);
static int __8b10b_clock_recovery_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
{
if (rd_interval > 4)

View File

@ -243,11 +243,36 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state,
struct drm_plane *plane)
{
u64 val;
plane_state->plane = plane;
plane_state->rotation = DRM_MODE_ROTATE_0;
plane_state->alpha = DRM_BLEND_ALPHA_OPAQUE;
plane_state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
if (plane->color_encoding_property) {
if (!drm_object_property_get_default_value(&plane->base,
plane->color_encoding_property,
&val))
plane_state->color_encoding = val;
}
if (plane->color_range_property) {
if (!drm_object_property_get_default_value(&plane->base,
plane->color_range_property,
&val))
plane_state->color_range = val;
}
if (plane->zpos_property) {
if (!drm_object_property_get_default_value(&plane->base,
plane->zpos_property,
&val)) {
plane_state->zpos = val;
plane_state->normalized_zpos = val;
}
}
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_state_reset);

View File

@ -297,11 +297,26 @@ int drm_object_property_set_value(struct drm_mode_object *obj,
}
EXPORT_SYMBOL(drm_object_property_set_value);
static int __drm_object_property_get_prop_value(struct drm_mode_object *obj,
struct drm_property *property,
uint64_t *val)
{
int i;
for (i = 0; i < obj->properties->count; i++) {
if (obj->properties->properties[i] == property) {
*val = obj->properties->values[i];
return 0;
}
}
return -EINVAL;
}
static int __drm_object_property_get_value(struct drm_mode_object *obj,
struct drm_property *property,
uint64_t *val)
{
int i;
/* read-only properties bypass atomic mechanism and still store
* their value in obj->properties->values[].. mostly to avoid
@ -311,15 +326,7 @@ static int __drm_object_property_get_value(struct drm_mode_object *obj,
!(property->flags & DRM_MODE_PROP_IMMUTABLE))
return drm_atomic_get_property(obj, property, val);
for (i = 0; i < obj->properties->count; i++) {
if (obj->properties->properties[i] == property) {
*val = obj->properties->values[i];
return 0;
}
}
return -EINVAL;
return __drm_object_property_get_prop_value(obj, property, val);
}
/**
@ -348,6 +355,32 @@ int drm_object_property_get_value(struct drm_mode_object *obj,
}
EXPORT_SYMBOL(drm_object_property_get_value);
/**
* drm_object_property_get_default_value - retrieve the default value of a
* property when in atomic mode.
* @obj: drm mode object to get property value from
* @property: property to retrieve
* @val: storage for the property value
*
* This function retrieves the default state of the given property as passed in
* to drm_object_attach_property
*
* Only atomic drivers should call this function directly, as for non-atomic
* drivers it will return the current value.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_object_property_get_default_value(struct drm_mode_object *obj,
struct drm_property *property,
uint64_t *val)
{
WARN_ON(!drm_drv_uses_atomic_modeset(property->dev));
return __drm_object_property_get_prop_value(obj, property, val);
}
EXPORT_SYMBOL(drm_object_property_get_default_value);
/* helper for getconnector and getproperties ioctls */
int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic,
uint32_t __user *prop_ptr,

View File

@ -35,6 +35,7 @@
#include <linux/list_sort.h>
#include <linux/export.h>
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
@ -127,7 +128,7 @@ EXPORT_SYMBOL(drm_mode_probed_add);
* according to the hdisplay, vdisplay, vrefresh.
* It is based from the VESA(TM) Coordinated Video Timing Generator by
* Graham Loveridge April 9, 2003 available at
* http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls
* http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls
*
* And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c.
* What I have done is to translate it by using integer calculation.
@ -727,6 +728,54 @@ int of_get_drm_display_mode(struct device_node *np,
return 0;
}
EXPORT_SYMBOL_GPL(of_get_drm_display_mode);
/**
* of_get_drm_panel_display_mode - get a panel-timing drm_display_mode from devicetree
* @np: device_node with the panel-timing specification
* @dmode: will be set to the return value
* @bus_flags: information about pixelclk, sync and DE polarity
*
* The Device Tree properties width-mm and height-mm will be read and set on
* the display mode if they are present.
*
* Returns:
* Zero on success, negative error code on failure.
*/
int of_get_drm_panel_display_mode(struct device_node *np,
struct drm_display_mode *dmode, u32 *bus_flags)
{
u32 width_mm = 0, height_mm = 0;
struct display_timing timing;
struct videomode vm;
int ret;
ret = of_get_display_timing(np, "panel-timing", &timing);
if (ret)
return ret;
videomode_from_timing(&timing, &vm);
memset(dmode, 0, sizeof(*dmode));
drm_display_mode_from_videomode(&vm, dmode);
if (bus_flags)
drm_bus_flags_from_videomode(&vm, bus_flags);
ret = of_property_read_u32(np, "width-mm", &width_mm);
if (ret && ret != -EINVAL)
return ret;
ret = of_property_read_u32(np, "height-mm", &height_mm);
if (ret && ret != -EINVAL)
return ret;
dmode->width_mm = width_mm;
dmode->height_mm = height_mm;
drm_mode_debug_printmodeline(dmode);
return 0;
}
EXPORT_SYMBOL_GPL(of_get_drm_panel_display_mode);
#endif /* CONFIG_OF */
#endif /* CONFIG_VIDEOMODE_HELPERS */

View File

@ -249,6 +249,21 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
if (panel)
*panel = NULL;
/**
* Devices can also be child nodes when we also control that device
* through the upstream device (ie, MIPI-DCS for a MIPI-DSI device).
*
* Lookup for a child node of the given parent that isn't either port
* or ports.
*/
for_each_available_child_of_node(np, remote) {
if (of_node_name_eq(remote, "port") ||
of_node_name_eq(remote, "ports"))
continue;
goto of_find_panel_or_bridge;
}
/*
* of_graph_get_remote_node() produces a noisy error message if port
* node isn't found and the absence of the port is a legit case here,
@ -259,6 +274,8 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
return -ENODEV;
remote = of_graph_get_remote_node(np, port, endpoint);
of_find_panel_or_bridge:
if (!remote)
return -ENODEV;

View File

@ -48,6 +48,8 @@ static void mdp5_plane_destroy(struct drm_plane *plane)
static void mdp5_plane_install_properties(struct drm_plane *plane,
struct drm_mode_object *obj)
{
unsigned int zpos;
drm_plane_create_rotation_property(plane,
DRM_MODE_ROTATE_0,
DRM_MODE_ROTATE_0 |
@ -59,7 +61,12 @@ static void mdp5_plane_install_properties(struct drm_plane *plane,
BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE));
drm_plane_create_zpos_property(plane, 1, 1, 255);
if (plane->type == DRM_PLANE_TYPE_PRIMARY)
zpos = STAGE_BASE;
else
zpos = STAGE0 + drm_plane_index(plane);
drm_plane_create_zpos_property(plane, zpos, 1, 255);
}
static void
@ -91,13 +98,6 @@ static void mdp5_plane_reset(struct drm_plane *plane)
kfree(to_mdp5_plane_state(plane->state));
mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL);
if (plane->type == DRM_PLANE_TYPE_PRIMARY)
mdp5_state->base.zpos = STAGE_BASE;
else
mdp5_state->base.zpos = STAGE0 + drm_plane_index(plane);
mdp5_state->base.normalized_zpos = mdp5_state->base.zpos;
__drm_atomic_helper_plane_reset(plane, &mdp5_state->base);
}

View File

@ -635,8 +635,6 @@ nv50_wndw_reset(struct drm_plane *plane)
plane->funcs->atomic_destroy_state(plane, plane->state);
__drm_atomic_helper_plane_reset(plane, &asyw->state);
plane->state->zpos = nv50_wndw_zpos_default(plane);
plane->state->normalized_zpos = nv50_wndw_zpos_default(plane);
}
static void

View File

@ -1,353 +0,0 @@
/*
* Copyright © 2010 Intel Corporation
* Copyright © 2010 Francisco Jerez <currojerez@riseup.net>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
/* Modified by Ben Skeggs <bskeggs@redhat.com> to match kernel list APIs */
#ifndef _XORG_LIST_H_
#define _XORG_LIST_H_
/**
* @file Classic doubly-link circular list implementation.
* For real usage examples of the linked list, see the file test/list.c
*
* Example:
* We need to keep a list of struct foo in the parent struct bar, i.e. what
* we want is something like this.
*
* struct bar {
* ...
* struct foo *list_of_foos; -----> struct foo {}, struct foo {}, struct foo{}
* ...
* }
*
* We need one list head in bar and a list element in all list_of_foos (both are of
* data type 'struct list_head').
*
* struct bar {
* ...
* struct list_head list_of_foos;
* ...
* }
*
* struct foo {
* ...
* struct list_head entry;
* ...
* }
*
* Now we initialize the list head:
*
* struct bar bar;
* ...
* INIT_LIST_HEAD(&bar.list_of_foos);
*
* Then we create the first element and add it to this list:
*
* struct foo *foo = malloc(...);
* ....
* list_add(&foo->entry, &bar.list_of_foos);
*
* Repeat the above for each element you want to add to the list. Deleting
* works with the element itself.
* list_del(&foo->entry);
* free(foo);
*
* Note: calling list_del(&bar.list_of_foos) will set bar.list_of_foos to an empty
* list again.
*
* Looping through the list requires a 'struct foo' as iterator and the
* name of the field the subnodes use.
*
* struct foo *iterator;
* list_for_each_entry(iterator, &bar.list_of_foos, entry) {
* if (iterator->something == ...)
* ...
* }
*
* Note: You must not call list_del() on the iterator if you continue the
* loop. You need to run the safe for-each loop instead:
*
* struct foo *iterator, *next;
* list_for_each_entry_safe(iterator, next, &bar.list_of_foos, entry) {
* if (...)
* list_del(&iterator->entry);
* }
*
*/
/**
* The linkage struct for list nodes. This struct must be part of your
* to-be-linked struct. struct list_head is required for both the head of the
* list and for each list node.
*
* Position and name of the struct list_head field is irrelevant.
* There are no requirements that elements of a list are of the same type.
* There are no requirements for a list head, any struct list_head can be a list
* head.
*/
struct list_head {
struct list_head *next, *prev;
};
/**
* Initialize the list as an empty list.
*
* Example:
* INIT_LIST_HEAD(&bar->list_of_foos);
*
* @param The list to initialized.
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void
INIT_LIST_HEAD(struct list_head *list)
{
list->next = list->prev = list;
}
static inline void
__list_add(struct list_head *entry,
struct list_head *prev, struct list_head *next)
{
next->prev = entry;
entry->next = next;
entry->prev = prev;
prev->next = entry;
}
/**
* Insert a new element after the given list head. The new element does not
* need to be initialised as empty list.
* The list changes from:
* head some element ...
* to
* head new element older element ...
*
* Example:
* struct foo *newfoo = malloc(...);
* list_add(&newfoo->entry, &bar->list_of_foos);
*
* @param entry The new element to prepend to the list.
* @param head The existing list.
*/
static inline void
list_add(struct list_head *entry, struct list_head *head)
{
__list_add(entry, head, head->next);
}
/**
* Append a new element to the end of the list given with this list head.
*
* The list changes from:
* head some element ... lastelement
* to
* head some element ... lastelement new element
*
* Example:
* struct foo *newfoo = malloc(...);
* list_add_tail(&newfoo->entry, &bar->list_of_foos);
*
* @param entry The new element to prepend to the list.
* @param head The existing list.
*/
static inline void
list_add_tail(struct list_head *entry, struct list_head *head)
{
__list_add(entry, head->prev, head);
}
static inline void
__list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
/**
* Remove the element from the list it is in. Using this function will reset
* the pointers to/from this element so it is removed from the list. It does
* NOT free the element itself or manipulate it otherwise.
*
* Using list_del on a pure list head (like in the example at the top of
* this file) will NOT remove the first element from
* the list but rather reset the list as empty list.
*
* Example:
* list_del(&foo->entry);
*
* @param entry The element to remove.
*/
static inline void
list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void
list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}
/**
* Check if the list is empty.
*
* Example:
* list_empty(&bar->list_of_foos);
*
* @return True if the list contains one or more elements or False otherwise.
*/
static inline bool
list_empty(struct list_head *head)
{
return head->next == head;
}
/**
* Returns a pointer to the container of this list element.
*
* Example:
* struct foo* f;
* f = container_of(&foo->entry, struct foo, entry);
* assert(f == foo);
*
* @param ptr Pointer to the struct list_head.
* @param type Data type of the list element.
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the data struct containing the list head.
*/
#ifndef container_of
#define container_of(ptr, type, member) \
(type *)((char *)(ptr) - (char *) &((type *)0)->member)
#endif
/**
* Alias of container_of
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* Retrieve the first list entry for the given list pointer.
*
* Example:
* struct foo *first;
* first = list_first_entry(&bar->list_of_foos, struct foo, list_of_foos);
*
* @param ptr The list head
* @param type Data type of the list element to retrieve
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the first list element.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* Retrieve the last list entry for the given listpointer.
*
* Example:
* struct foo *first;
* first = list_last_entry(&bar->list_of_foos, struct foo, list_of_foos);
*
* @param ptr The list head
* @param type Data type of the list element to retrieve
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the last list element.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
#define __container_of(ptr, sample, member) \
(void *)container_of((ptr), typeof(*(sample)), member)
/**
* Loop through the list given by head and set pos to struct in the list.
*
* Example:
* struct foo *iterator;
* list_for_each_entry(iterator, &bar->list_of_foos, entry) {
* [modify iterator]
* }
*
* This macro is not safe for node deletion. Use list_for_each_entry_safe
* instead.
*
* @param pos Iterator variable of the type of the list elements.
* @param head List head
* @param member Member name of the struct list_head in the list elements.
*
*/
#define list_for_each_entry(pos, head, member) \
for (pos = __container_of((head)->next, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.next, pos, member))
/**
* Loop through the list, keeping a backup pointer to the element. This
* macro allows for the deletion of a list element while looping through the
* list.
*
* See list_for_each_entry for more details.
*/
#define list_for_each_entry_safe(pos, tmp, head, member) \
for (pos = __container_of((head)->next, pos, member), \
tmp = __container_of(pos->member.next, pos, member); \
&pos->member != (head); \
pos = tmp, tmp = __container_of(pos->member.next, tmp, member))
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = __container_of((head)->prev, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.prev, pos, member))
#define list_for_each_entry_continue(pos, head, member) \
for (pos = __container_of(pos->member.next, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.next, pos, member))
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = __container_of(pos->member.prev, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.prev, pos, member))
#define list_for_each_entry_from(pos, head, member) \
for (; \
&pos->member != (head); \
pos = __container_of(pos->member.next, pos, member))
#endif

View File

@ -403,7 +403,6 @@ void omap_plane_install_properties(struct drm_plane *plane,
static void omap_plane_reset(struct drm_plane *plane)
{
struct omap_plane *omap_plane = to_omap_plane(plane);
struct omap_plane_state *omap_state;
if (plane->state)
@ -414,15 +413,6 @@ static void omap_plane_reset(struct drm_plane *plane)
return;
__drm_atomic_helper_plane_reset(plane, &omap_state->base);
/*
* Set the zpos default depending on whether we are a primary or overlay
* plane.
*/
plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY
? 0 : omap_plane->id;
plane->state->color_encoding = DRM_COLOR_YCBCR_BT601;
plane->state->color_range = DRM_COLOR_YCBCR_FULL_RANGE;
}
static struct drm_plane_state *
@ -533,6 +523,7 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
unsigned int num_planes = dispc_get_num_ovls(priv->dispc);
struct drm_plane *plane;
struct omap_plane *omap_plane;
unsigned int zpos;
int ret;
u32 nformats;
const u32 *formats;
@ -564,7 +555,16 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
drm_plane_helper_add(plane, &omap_plane_helper_funcs);
omap_plane_install_properties(plane, &plane->base);
drm_plane_create_zpos_property(plane, 0, 0, num_planes - 1);
/*
* Set the zpos default depending on whether we are a primary or overlay
* plane.
*/
if (plane->type == DRM_PLANE_TYPE_PRIMARY)
zpos = 0;
else
zpos = omap_plane->id;
drm_plane_create_zpos_property(plane, zpos, 0, num_planes - 1);
drm_plane_create_alpha_property(plane);
drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE));

View File

@ -3058,6 +3058,7 @@ static const struct drm_display_mode rocktech_rk101ii01d_ct_mode = {
static const struct panel_desc rocktech_rk101ii01d_ct = {
.modes = &rocktech_rk101ii01d_ct_mode,
.bpc = 8,
.num_modes = 1,
.size = {
.width = 217,

View File

@ -562,7 +562,7 @@ static int panfrost_probe(struct platform_device *pdev)
pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
/* Allocate and initialze the DRM device. */
/* Allocate and initialize the DRM device. */
ddev = drm_dev_alloc(&panfrost_drm_driver, &pdev->dev);
if (IS_ERR(ddev))
return PTR_ERR(ddev);

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 */
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2019 Arm Ltd.
*
* Based on msm_gem_freedreno.c:

View File

@ -14,7 +14,7 @@
*/
enum panfrost_hw_issue {
/* Need way to guarantee that all previously-translated memory accesses
* are commited */
* are committed */
HW_ISSUE_6367,
/* On job complete with non-done the cache is not flushed */

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
#include <drm/panfrost_drm.h>

View File

@ -293,7 +293,7 @@
#define AS_FAULTADDRESS_LO(as) (MMU_AS(as) + 0x20) /* (RO) Fault Address for address space n, low word */
#define AS_FAULTADDRESS_HI(as) (MMU_AS(as) + 0x24) /* (RO) Fault Address for address space n, high word */
#define AS_STATUS(as) (MMU_AS(as) + 0x28) /* (RO) Status flags for address space n */
/* Additional Bifrost AS regsiters */
/* Additional Bifrost AS registers */
#define AS_TRANSCFG_LO(as) (MMU_AS(as) + 0x30) /* (RW) Translation table configuration for address space n, low word */
#define AS_TRANSCFG_HI(as) (MMU_AS(as) + 0x34) /* (RW) Translation table configuration for address space n, high word */
#define AS_FAULTEXTRA_LO(as) (MMU_AS(as) + 0x38) /* (RO) Secondary fault address for address space n, low word */

View File

@ -704,7 +704,6 @@ static void rcar_du_plane_reset(struct drm_plane *plane)
state->hwindex = -1;
state->source = RCAR_DU_PLANE_MEMORY;
state->colorkey = RCAR_DU_COLORKEY_NONE;
state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
}
static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,

View File

@ -353,7 +353,6 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
return;
__drm_atomic_helper_plane_reset(plane, &state->state);
state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
}
static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {

View File

@ -579,7 +579,7 @@ static const struct drm_simple_display_pipe_funcs ssd130x_pipe_funcs = {
static int ssd130x_connector_get_modes(struct drm_connector *connector)
{
struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev);
struct drm_display_mode *mode = &ssd130x->mode;
struct drm_display_mode *mode;
struct device *dev = ssd130x->dev;
mode = drm_mode_duplicate(connector->dev, &ssd130x->mode);

View File

@ -351,7 +351,7 @@ static const struct drm_plane_funcs sti_cursor_plane_helpers_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = sti_plane_reset,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.late_register = sti_cursor_late_register,

View File

@ -905,7 +905,7 @@ static const struct drm_plane_funcs sti_gdp_plane_helpers_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = sti_plane_reset,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.late_register = sti_gdp_late_register,

View File

@ -1283,7 +1283,7 @@ static const struct drm_plane_funcs sti_hqvdp_plane_helpers_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = sti_plane_reset,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.late_register = sti_hqvdp_late_register,

View File

@ -112,12 +112,6 @@ static int sti_plane_get_default_zpos(enum drm_plane_type type)
return 0;
}
void sti_plane_reset(struct drm_plane *plane)
{
drm_atomic_helper_plane_reset(plane);
plane->state->zpos = sti_plane_get_default_zpos(plane->type);
}
static void sti_plane_attach_zorder_property(struct drm_plane *drm_plane,
enum drm_plane_type type)
{

View File

@ -81,5 +81,4 @@ void sti_plane_update_fps(struct sti_plane *plane,
void sti_plane_init_property(struct sti_plane *plane,
enum drm_plane_type type);
void sti_plane_reset(struct drm_plane *plane);
#endif

View File

@ -77,6 +77,7 @@
#define LTDC_CPSR 0x0044 /* Current Position Status */
#define LTDC_CDSR 0x0048 /* Current Display Status */
#define LTDC_EDCR 0x0060 /* External Display Control */
#define LTDC_CCRCR 0x007C /* Computed CRC value */
#define LTDC_FUT 0x0090 /* Fifo underrun Threshold */
/* Layer register offsets */
@ -121,6 +122,7 @@
#define GCR_LTDCEN BIT(0) /* LTDC ENable */
#define GCR_DEN BIT(16) /* Dither ENable */
#define GCR_CRCEN BIT(19) /* CRC ENable */
#define GCR_PCPOL BIT(28) /* Pixel Clock POLarity-Inverted */
#define GCR_DEPOL BIT(29) /* Data Enable POLarity-High */
#define GCR_VSPOL BIT(30) /* Vertical Synchro POLarity-High */
@ -227,6 +229,13 @@
#define NB_PF 8 /* Max nb of HW pixel format */
/*
* Skip the first value and the second in case CRC was enabled during
* the thread irq. This is to be sure CRC value is relevant for the
* frame.
*/
#define CRC_SKIP_FRAMES 2
enum ltdc_pix_fmt {
PF_NONE,
/* RGB formats */
@ -624,7 +633,8 @@ static inline void ltdc_set_ycbcr_config(struct drm_plane *plane, u32 drm_pix_fm
break;
default:
/* RGB or not a YCbCr supported format */
break;
DRM_ERROR("Unsupported pixel format: %u\n", drm_pix_fmt);
return;
}
/* Enable limited range */
@ -664,6 +674,26 @@ static inline void ltdc_set_ycbcr_coeffs(struct drm_plane *plane)
ltdc_ycbcr2rgb_coeffs[enc][ran][1]);
}
static inline void ltdc_irq_crc_handle(struct ltdc_device *ldev,
struct drm_crtc *crtc)
{
u32 crc;
int ret;
if (ldev->crc_skip_count < CRC_SKIP_FRAMES) {
ldev->crc_skip_count++;
return;
}
/* Get the CRC of the frame */
ret = regmap_read(ldev->regmap, LTDC_CCRCR, &crc);
if (ret)
return;
/* Report to DRM the CRC (hw dependent feature) */
drm_crtc_add_crc_entry(crtc, true, drm_crtc_accurate_vblank_count(crtc), &crc);
}
static irqreturn_t ltdc_irq_thread(int irq, void *arg)
{
struct drm_device *ddev = arg;
@ -671,9 +701,14 @@ static irqreturn_t ltdc_irq_thread(int irq, void *arg)
struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
/* Line IRQ : trigger the vblank event */
if (ldev->irq_status & ISR_LIF)
if (ldev->irq_status & ISR_LIF) {
drm_crtc_handle_vblank(crtc);
/* Early return if CRC is not active */
if (ldev->crc_active)
ltdc_irq_crc_handle(ldev, crtc);
}
/* Save FIFO Underrun & Transfer Error status */
mutex_lock(&ldev->err_lock);
if (ldev->irq_status & ISR_FUIF)
@ -1079,6 +1114,48 @@ static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc)
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE);
}
static int ltdc_crtc_set_crc_source(struct drm_crtc *crtc, const char *source)
{
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
int ret;
DRM_DEBUG_DRIVER("\n");
if (!crtc)
return -ENODEV;
if (source && strcmp(source, "auto") == 0) {
ldev->crc_active = true;
ret = regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
} else if (!source) {
ldev->crc_active = false;
ret = regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
} else {
ret = -EINVAL;
}
ldev->crc_skip_count = 0;
return ret;
}
static int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc,
const char *source, size_t *values_cnt)
{
DRM_DEBUG_DRIVER("\n");
if (!crtc)
return -ENODEV;
if (source && strcmp(source, "auto") != 0) {
DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",
source, crtc->name);
return -EINVAL;
}
*values_cnt = 1;
return 0;
}
static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
@ -1091,6 +1168,20 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
};
static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = ltdc_crtc_enable_vblank,
.disable_vblank = ltdc_crtc_disable_vblank,
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
.set_crc_source = ltdc_crtc_set_crc_source,
.verify_crc_source = ltdc_crtc_verify_crc_source,
};
/*
* DRM_PLANE
*/
@ -1478,8 +1569,13 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
drm_plane_create_zpos_immutable_property(primary, 0);
ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
&ltdc_crtc_funcs, NULL);
/* Init CRTC according to its hardware features */
if (ldev->caps.crc)
ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
&ltdc_crtc_with_crc_support_funcs, NULL);
else
ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
&ltdc_crtc_funcs, NULL);
if (ret) {
DRM_ERROR("Can not initialize CRTC\n");
goto cleanup;
@ -1629,6 +1725,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.ycbcr_input = false;
ldev->caps.ycbcr_output = false;
ldev->caps.plane_reg_shadow = false;
ldev->caps.crc = false;
break;
case HWVER_20101:
ldev->caps.layer_ofs = LAY_OFS_0;
@ -1643,6 +1740,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.ycbcr_input = false;
ldev->caps.ycbcr_output = false;
ldev->caps.plane_reg_shadow = false;
ldev->caps.crc = false;
break;
case HWVER_40100:
ldev->caps.layer_ofs = LAY_OFS_1;
@ -1657,6 +1755,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
ldev->caps.ycbcr_input = true;
ldev->caps.ycbcr_output = true;
ldev->caps.plane_reg_shadow = true;
ldev->caps.crc = true;
break;
default:
return -ENODEV;

View File

@ -27,6 +27,7 @@ struct ltdc_caps {
bool ycbcr_input; /* ycbcr input converter supported */
bool ycbcr_output; /* ycbcr output converter supported */
bool plane_reg_shadow; /* plane shadow registers ability */
bool crc; /* cyclic redundancy check supported */
};
#define LTDC_MAX_LAYER 4
@ -46,6 +47,8 @@ struct ltdc_device {
u32 irq_status;
struct fps_info plane_fpsi[LTDC_MAX_LAYER];
struct drm_atomic_state *suspend_state;
int crc_skip_count;
bool crc_active;
};
int ltdc_load(struct drm_device *ddev);

View File

@ -18,7 +18,6 @@
static void sun4i_backend_layer_reset(struct drm_plane *plane)
{
struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
struct sun4i_layer_state *state;
if (plane->state) {
@ -31,10 +30,8 @@ static void sun4i_backend_layer_reset(struct drm_plane *plane)
}
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state) {
if (state)
__drm_atomic_helper_plane_reset(plane, &state->state);
plane->state->zpos = layer->id;
}
}
static struct drm_plane_state *
@ -192,7 +189,8 @@ static const uint64_t sun4i_layer_modifiers[] = {
static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
struct sun4i_backend *backend,
enum drm_plane_type type)
enum drm_plane_type type,
unsigned int id)
{
const uint64_t *modifiers = sun4i_layer_modifiers;
const uint32_t *formats = sun4i_layer_formats;
@ -204,6 +202,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
if (!layer)
return ERR_PTR(-ENOMEM);
layer->id = id;
layer->backend = backend;
if (IS_ERR_OR_NULL(backend->frontend)) {
@ -226,8 +225,8 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
&sun4i_backend_layer_helper_funcs);
drm_plane_create_alpha_property(&layer->plane);
drm_plane_create_zpos_property(&layer->plane, 0, 0,
SUN4I_BACKEND_NUM_LAYERS - 1);
drm_plane_create_zpos_property(&layer->plane, layer->id,
0, SUN4I_BACKEND_NUM_LAYERS - 1);
return layer;
}
@ -249,14 +248,13 @@ struct drm_plane **sun4i_layers_init(struct drm_device *drm,
enum drm_plane_type type = i ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY;
struct sun4i_layer *layer;
layer = sun4i_layer_init_one(drm, backend, type);
layer = sun4i_layer_init_one(drm, backend, type, i);
if (IS_ERR(layer)) {
dev_err(drm->dev, "Couldn't initialize %s plane\n",
i ? "overlay" : "primary");
return ERR_CAST(layer);
}
layer->id = i;
planes[i] = &layer->plane;
}

View File

@ -549,6 +549,15 @@ static void drm_dp_link_get_adjustments(struct drm_dp_link *link,
{
struct drm_dp_link_train_set *adjust = &link->train.adjust;
unsigned int i;
u8 post_cursor;
int err;
err = drm_dp_dpcd_read(link->aux, DP_ADJUST_REQUEST_POST_CURSOR2,
&post_cursor, sizeof(post_cursor));
if (err < 0) {
DRM_ERROR("failed to read post_cursor2: %d\n", err);
post_cursor = 0;
}
for (i = 0; i < link->lanes; i++) {
adjust->voltage_swing[i] =
@ -560,7 +569,7 @@ static void drm_dp_link_get_adjustments(struct drm_dp_link *link,
DP_TRAIN_PRE_EMPHASIS_SHIFT;
adjust->post_cursor[i] =
drm_dp_get_adjust_request_post_cursor(status, i);
(post_cursor >> (i << 1)) & 0x3;
}
}

View File

@ -51,6 +51,21 @@ config DRM_GM12U320
This is a KMS driver for projectors which use the GM12U320 chipset
for video transfer over USB2/3, such as the Acer C120 mini projector.
config DRM_PANEL_MIPI_DBI
tristate "DRM support for MIPI DBI compatible panels"
depends on DRM && SPI
select DRM_KMS_HELPER
select DRM_GEM_CMA_HELPER
select DRM_MIPI_DBI
select BACKLIGHT_CLASS_DEVICE
select VIDEOMODE_HELPERS
help
Say Y here if you want to enable support for MIPI DBI compatible
panels. The controller command setup can be provided using a
firmware file. For more information see
https://github.com/notro/panel-mipi-dbi/wiki.
To compile this driver as a module, choose M here.
config DRM_SIMPLEDRM
tristate "Simple framebuffer driver"
depends on DRM && MMU

View File

@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o
obj-$(CONFIG_DRM_BOCHS) += bochs.o
obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o
obj-$(CONFIG_DRM_GM12U320) += gm12u320.o
obj-$(CONFIG_DRM_PANEL_MIPI_DBI) += panel-mipi-dbi.o
obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm.o
obj-$(CONFIG_TINYDRM_HX8357D) += hx8357d.o
obj-$(CONFIG_TINYDRM_ILI9163) += ili9163.o

View File

@ -0,0 +1,398 @@
// SPDX-License-Identifier: GPL-2.0
/*
* DRM driver for MIPI DBI compatible display panels
*
* Copyright 2022 Noralf Trønnes
*/
#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_modes.h>
#include <drm/drm_modeset_helper.h>
#include <video/mipi_display.h>
static const u8 panel_mipi_dbi_magic[15] = { 'M', 'I', 'P', 'I', ' ', 'D', 'B', 'I',
0, 0, 0, 0, 0, 0, 0 };
/*
* The display controller configuration is stored in a firmware file.
* The Device Tree 'compatible' property value with a '.bin' suffix is passed
* to request_firmware() to fetch this file.
*/
struct panel_mipi_dbi_config {
/* Magic string: panel_mipi_dbi_magic */
u8 magic[15];
/* Config file format version */
u8 file_format_version;
/*
* MIPI commands to execute when the display pipeline is enabled.
* This is used to configure the display controller.
*
* The commands are stored in a byte array with the format:
* command, num_parameters, [ parameter, ...], command, ...
*
* Some commands require a pause before the next command can be received.
* Inserting a delay in the command sequence is done by using the NOP command with one
* parameter: delay in miliseconds (the No Operation command is part of the MIPI Display
* Command Set where it has no parameters).
*
* Example:
* command 0x11
* sleep 120ms
* command 0xb1 parameters 0x01, 0x2c, 0x2d
* command 0x29
*
* Byte sequence:
* 0x11 0x00
* 0x00 0x01 0x78
* 0xb1 0x03 0x01 0x2c 0x2d
* 0x29 0x00
*/
u8 commands[];
};
struct panel_mipi_dbi_commands {
const u8 *buf;
size_t len;
};
static struct panel_mipi_dbi_commands *
panel_mipi_dbi_check_commands(struct device *dev, const struct firmware *fw)
{
const struct panel_mipi_dbi_config *config = (struct panel_mipi_dbi_config *)fw->data;
struct panel_mipi_dbi_commands *commands;
size_t size = fw->size, commands_len;
unsigned int i = 0;
if (size < sizeof(*config) + 2) { /* At least 1 command */
dev_err(dev, "config: file size=%zu is too small\n", size);
return ERR_PTR(-EINVAL);
}
if (memcmp(config->magic, panel_mipi_dbi_magic, sizeof(config->magic))) {
dev_err(dev, "config: Bad magic: %15ph\n", config->magic);
return ERR_PTR(-EINVAL);
}
if (config->file_format_version != 1) {
dev_err(dev, "config: version=%u is not supported\n", config->file_format_version);
return ERR_PTR(-EINVAL);
}
drm_dev_dbg(dev, DRM_UT_DRIVER, "size=%zu version=%u\n", size, config->file_format_version);
commands_len = size - sizeof(*config);
while ((i + 1) < commands_len) {
u8 command = config->commands[i++];
u8 num_parameters = config->commands[i++];
const u8 *parameters = &config->commands[i];
i += num_parameters;
if (i > commands_len) {
dev_err(dev, "config: command=0x%02x num_parameters=%u overflows\n",
command, num_parameters);
return ERR_PTR(-EINVAL);
}
if (command == 0x00 && num_parameters == 1)
drm_dev_dbg(dev, DRM_UT_DRIVER, "sleep %ums\n", parameters[0]);
else
drm_dev_dbg(dev, DRM_UT_DRIVER, "command %02x %*ph\n",
command, num_parameters, parameters);
}
if (i != commands_len) {
dev_err(dev, "config: malformed command array\n");
return ERR_PTR(-EINVAL);
}
commands = devm_kzalloc(dev, sizeof(*commands), GFP_KERNEL);
if (!commands)
return ERR_PTR(-ENOMEM);
commands->len = commands_len;
commands->buf = devm_kmemdup(dev, config->commands, commands->len, GFP_KERNEL);
if (!commands->buf)
return ERR_PTR(-ENOMEM);
return commands;
}
static struct panel_mipi_dbi_commands *panel_mipi_dbi_commands_from_fw(struct device *dev)
{
struct panel_mipi_dbi_commands *commands;
const struct firmware *fw;
const char *compatible;
char fw_name[40];
int ret;
ret = of_property_read_string_index(dev->of_node, "compatible", 0, &compatible);
if (ret)
return ERR_PTR(ret);
snprintf(fw_name, sizeof(fw_name), "%s.bin", compatible);
ret = request_firmware(&fw, fw_name, dev);
if (ret) {
dev_err(dev, "No config file found for compatible '%s' (error=%d)\n",
compatible, ret);
return ERR_PTR(ret);
}
commands = panel_mipi_dbi_check_commands(dev, fw);
release_firmware(fw);
return commands;
}
static void panel_mipi_dbi_commands_execute(struct mipi_dbi *dbi,
struct panel_mipi_dbi_commands *commands)
{
unsigned int i = 0;
if (!commands)
return;
while (i < commands->len) {
u8 command = commands->buf[i++];
u8 num_parameters = commands->buf[i++];
const u8 *parameters = &commands->buf[i];
if (command == 0x00 && num_parameters == 1)
msleep(parameters[0]);
else if (num_parameters)
mipi_dbi_command_stackbuf(dbi, command, parameters, num_parameters);
else
mipi_dbi_command(dbi, command);
i += num_parameters;
}
}
static void panel_mipi_dbi_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
struct mipi_dbi *dbi = &dbidev->dbi;
int ret, idx;
if (!drm_dev_enter(pipe->crtc.dev, &idx))
return;
drm_dbg(pipe->crtc.dev, "\n");
ret = mipi_dbi_poweron_conditional_reset(dbidev);
if (ret < 0)
goto out_exit;
if (!ret)
panel_mipi_dbi_commands_execute(dbi, dbidev->driver_private);
mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
out_exit:
drm_dev_exit(idx);
}
static const struct drm_simple_display_pipe_funcs panel_mipi_dbi_pipe_funcs = {
.enable = panel_mipi_dbi_enable,
.disable = mipi_dbi_pipe_disable,
.update = mipi_dbi_pipe_update,
};
DEFINE_DRM_GEM_CMA_FOPS(panel_mipi_dbi_fops);
static const struct drm_driver panel_mipi_dbi_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &panel_mipi_dbi_fops,
DRM_GEM_CMA_DRIVER_OPS_VMAP,
.debugfs_init = mipi_dbi_debugfs_init,
.name = "panel-mipi-dbi",
.desc = "MIPI DBI compatible display panel",
.date = "20220103",
.major = 1,
.minor = 0,
};
static int panel_mipi_dbi_get_mode(struct mipi_dbi_dev *dbidev, struct drm_display_mode *mode)
{
struct device *dev = dbidev->drm.dev;
u16 hback_porch, vback_porch;
int ret;
ret = of_get_drm_panel_display_mode(dev->of_node, mode, NULL);
if (ret) {
dev_err(dev, "%pOF: failed to get panel-timing (error=%d)\n", dev->of_node, ret);
return ret;
}
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
hback_porch = mode->htotal - mode->hsync_end;
vback_porch = mode->vtotal - mode->vsync_end;
/*
* Make sure width and height are set and that only back porch and
* pixelclock are set in the other timing values. Also check that
* width and height don't exceed the 16-bit value specified by MIPI DCS.
*/
if (!mode->hdisplay || !mode->vdisplay || mode->flags ||
mode->hsync_end > mode->hdisplay || (hback_porch + mode->hdisplay) > 0xffff ||
mode->vsync_end > mode->vdisplay || (vback_porch + mode->vdisplay) > 0xffff) {
dev_err(dev, "%pOF: panel-timing out of bounds\n", dev->of_node);
return -EINVAL;
}
/* The driver doesn't use the pixel clock but it is mandatory so fake one if not set */
if (!mode->clock)
mode->clock = mode->htotal * mode->vtotal * 60 / 1000;
dbidev->top_offset = vback_porch;
dbidev->left_offset = hback_porch;
return 0;
}
static int panel_mipi_dbi_spi_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct drm_display_mode mode;
struct mipi_dbi_dev *dbidev;
struct drm_device *drm;
struct mipi_dbi *dbi;
struct gpio_desc *dc;
int ret;
dbidev = devm_drm_dev_alloc(dev, &panel_mipi_dbi_driver, struct mipi_dbi_dev, drm);
if (IS_ERR(dbidev))
return PTR_ERR(dbidev);
dbi = &dbidev->dbi;
drm = &dbidev->drm;
ret = panel_mipi_dbi_get_mode(dbidev, &mode);
if (ret)
return ret;
dbidev->regulator = devm_regulator_get(dev, "power");
if (IS_ERR(dbidev->regulator))
return dev_err_probe(dev, PTR_ERR(dbidev->regulator),
"Failed to get regulator 'power'\n");
dbidev->backlight = devm_of_find_backlight(dev);
if (IS_ERR(dbidev->backlight))
return dev_err_probe(dev, PTR_ERR(dbidev->backlight), "Failed to get backlight\n");
dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(dbi->reset))
return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW);
if (IS_ERR(dc))
return dev_err_probe(dev, PTR_ERR(dc), "Failed to get GPIO 'dc'\n");
ret = mipi_dbi_spi_init(spi, dbi, dc);
if (ret)
return ret;
if (device_property_present(dev, "write-only"))
dbi->read_commands = NULL;
dbidev->driver_private = panel_mipi_dbi_commands_from_fw(dev);
if (IS_ERR(dbidev->driver_private))
return PTR_ERR(dbidev->driver_private);
ret = mipi_dbi_dev_init(dbidev, &panel_mipi_dbi_pipe_funcs, &mode, 0);
if (ret)
return ret;
drm_mode_config_reset(drm);
ret = drm_dev_register(drm, 0);
if (ret)
return ret;
spi_set_drvdata(spi, drm);
drm_fbdev_generic_setup(drm, 0);
return 0;
}
static int panel_mipi_dbi_spi_remove(struct spi_device *spi)
{
struct drm_device *drm = spi_get_drvdata(spi);
drm_dev_unplug(drm);
drm_atomic_helper_shutdown(drm);
return 0;
}
static void panel_mipi_dbi_spi_shutdown(struct spi_device *spi)
{
drm_atomic_helper_shutdown(spi_get_drvdata(spi));
}
static int __maybe_unused panel_mipi_dbi_pm_suspend(struct device *dev)
{
return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
}
static int __maybe_unused panel_mipi_dbi_pm_resume(struct device *dev)
{
drm_mode_config_helper_resume(dev_get_drvdata(dev));
return 0;
}
static const struct dev_pm_ops panel_mipi_dbi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(panel_mipi_dbi_pm_suspend, panel_mipi_dbi_pm_resume)
};
static const struct of_device_id panel_mipi_dbi_spi_of_match[] = {
{ .compatible = "panel-mipi-dbi-spi" },
{},
};
MODULE_DEVICE_TABLE(of, panel_mipi_dbi_spi_of_match);
static const struct spi_device_id panel_mipi_dbi_spi_id[] = {
{ "panel-mipi-dbi-spi", 0 },
{ },
};
MODULE_DEVICE_TABLE(spi, panel_mipi_dbi_spi_id);
static struct spi_driver panel_mipi_dbi_spi_driver = {
.driver = {
.name = "panel-mipi-dbi-spi",
.owner = THIS_MODULE,
.of_match_table = panel_mipi_dbi_spi_of_match,
.pm = &panel_mipi_dbi_pm_ops,
},
.id_table = panel_mipi_dbi_spi_id,
.probe = panel_mipi_dbi_spi_probe,
.remove = panel_mipi_dbi_spi_remove,
.shutdown = panel_mipi_dbi_spi_shutdown,
};
module_spi_driver(panel_mipi_dbi_spi_driver);
MODULE_DESCRIPTION("MIPI DBI compatible display panel driver");
MODULE_AUTHOR("Noralf Trønnes");
MODULE_LICENSE("GPL");

View File

@ -508,26 +508,6 @@ static void repaper_get_temperature(struct repaper_epd *epd)
epd->factored_stage_time = epd->stage_time * factor10x / 10;
}
static void repaper_gray8_to_mono_reversed(u8 *buf, u32 width, u32 height)
{
u8 *gray8 = buf, *mono = buf;
int y, xb, i;
for (y = 0; y < height; y++)
for (xb = 0; xb < width / 8; xb++) {
u8 byte = 0x00;
for (i = 0; i < 8; i++) {
int x = xb * 8 + i;
byte >>= 1;
if (gray8[y * width + x] >> 7)
byte |= BIT(7);
}
*mono++ = byte;
}
}
static int repaper_fb_dirty(struct drm_framebuffer *fb)
{
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
@ -560,12 +540,10 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
if (ret)
goto out_free;
drm_fb_xrgb8888_to_gray8(buf, 0, cma_obj->vaddr, fb, &clip);
drm_fb_xrgb8888_to_mono_reversed(buf, 0, cma_obj->vaddr, fb, &clip);
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
repaper_gray8_to_mono_reversed(buf, fb->width, fb->height);
if (epd->partial) {
repaper_frame_data_repeat(epd, buf, epd->current_frame,
REPAPER_NORMAL);

View File

@ -810,6 +810,9 @@ static int simpledrm_device_init_modeset(struct simpledrm_device *sdev)
if (ret)
return ret;
drm_connector_helper_add(connector, &simpledrm_connector_helper_funcs);
drm_connector_set_panel_orientation_with_quirk(connector,
DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
mode->hdisplay, mode->vdisplay);
formats = simpledrm_device_formats(sdev, &nformats);

View File

@ -392,34 +392,24 @@ v3d_sched_init(struct v3d_dev *v3d)
hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_bin", v3d->drm.dev);
if (ret) {
dev_err(v3d->drm.dev, "Failed to create bin scheduler: %d.", ret);
if (ret)
return ret;
}
ret = drm_sched_init(&v3d->queue[V3D_RENDER].sched,
&v3d_render_sched_ops,
hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_render", v3d->drm.dev);
if (ret) {
dev_err(v3d->drm.dev, "Failed to create render scheduler: %d.",
ret);
v3d_sched_fini(v3d);
return ret;
}
if (ret)
goto fail;
ret = drm_sched_init(&v3d->queue[V3D_TFU].sched,
&v3d_tfu_sched_ops,
hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_tfu", v3d->drm.dev);
if (ret) {
dev_err(v3d->drm.dev, "Failed to create TFU scheduler: %d.",
ret);
v3d_sched_fini(v3d);
return ret;
}
if (ret)
goto fail;
if (v3d_has_csd(v3d)) {
ret = drm_sched_init(&v3d->queue[V3D_CSD].sched,
@ -427,27 +417,23 @@ v3d_sched_init(struct v3d_dev *v3d)
hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_csd", v3d->drm.dev);
if (ret) {
dev_err(v3d->drm.dev, "Failed to create CSD scheduler: %d.",
ret);
v3d_sched_fini(v3d);
return ret;
}
if (ret)
goto fail;
ret = drm_sched_init(&v3d->queue[V3D_CACHE_CLEAN].sched,
&v3d_cache_clean_sched_ops,
hw_jobs_limit, job_hang_limit,
msecs_to_jiffies(hang_limit_ms), NULL,
NULL, "v3d_cache_clean", v3d->drm.dev);
if (ret) {
dev_err(v3d->drm.dev, "Failed to create CACHE_CLEAN scheduler: %d.",
ret);
v3d_sched_fini(v3d);
return ret;
}
if (ret)
goto fail;
}
return 0;
fail:
v3d_sched_fini(v3d);
return ret;
}
void

View File

@ -16,15 +16,15 @@
* must be laid out exactly in the same format as the framebuffer. Yes I know
* their are cards with hardware that coverts images of various depths to the
* framebuffer depth. But not every card has this. All images must be rounded
* up to the nearest byte. For example a bitmap 12 bits wide must be two
* bytes width.
* up to the nearest byte. For example a bitmap 12 bits wide must be two
* bytes width.
*
* Tony:
* Incorporate mask tables similar to fbcon-cfb*.c in 2.4 API. This speeds
* Tony:
* Incorporate mask tables similar to fbcon-cfb*.c in 2.4 API. This speeds
* up the code significantly.
*
*
* Code for depths not multiples of BITS_PER_LONG is still kludgy, which is
* still processed a bit at a time.
* still processed a bit at a time.
*
* Also need to add code to deal with cards endians that are different than
* the native cpu endians. I also need to deal with MSB position in the word.
@ -72,8 +72,8 @@ static const u32 cfb_tab32[] = {
#define FB_WRITEL fb_writel
#define FB_READL fb_readl
static inline void color_imageblit(const struct fb_image *image,
struct fb_info *p, u8 __iomem *dst1,
static inline void color_imageblit(const struct fb_image *image,
struct fb_info *p, u8 __iomem *dst1,
u32 start_index,
u32 pitch_index)
{
@ -92,7 +92,7 @@ static inline void color_imageblit(const struct fb_image *image,
dst = (u32 __iomem *) dst1;
shift = 0;
val = 0;
if (start_index) {
u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
start_index, bswapmask);
@ -109,8 +109,8 @@ static inline void color_imageblit(const struct fb_image *image,
val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
if (shift >= null_bits) {
FB_WRITEL(val, dst++);
val = (shift == null_bits) ? 0 :
val = (shift == null_bits) ? 0 :
FB_SHIFT_LOW(p, color, 32 - shift);
}
shift += bpp;
@ -134,9 +134,9 @@ static inline void color_imageblit(const struct fb_image *image,
}
}
static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p,
static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p,
u8 __iomem *dst1, u32 fgcolor,
u32 bgcolor,
u32 bgcolor,
u32 start_index,
u32 pitch_index)
{
@ -172,7 +172,7 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info *
l--;
color = (*s & (1 << l)) ? fgcolor : bgcolor;
val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
/* Did the bitshift spill bits to the next long? */
if (shift >= null_bits) {
FB_WRITEL(val, dst++);
@ -191,16 +191,16 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info *
FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
}
dst1 += pitch;
src += spitch;
src += spitch;
if (pitch_index) {
dst2 += pitch;
dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
start_index += pitch_index;
start_index &= 32 - 1;
}
}
}
@ -212,29 +212,35 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info *
* fix->line_legth is divisible by 4;
* beginning and end of a scanline is dword aligned
*/
static inline void fast_imageblit(const struct fb_image *image, struct fb_info *p,
u8 __iomem *dst1, u32 fgcolor,
u32 bgcolor)
static inline void fast_imageblit(const struct fb_image *image, struct fb_info *p,
u8 __iomem *dst1, u32 fgcolor,
u32 bgcolor)
{
u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
u32 bit_mask, end_mask, eorx, shift;
u32 bit_mask, eorx;
const char *s = image->data, *src;
u32 __iomem *dst;
const u32 *tab = NULL;
size_t tablen;
u32 colortab[16];
int i, j, k;
switch (bpp) {
case 8:
tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
tablen = 16;
break;
case 16:
tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
tablen = 4;
break;
case 32:
default:
tab = cfb_tab32;
tablen = 2;
break;
default:
return;
}
for (i = ppw-1; i--; ) {
@ -243,25 +249,52 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info *
fgx |= fgcolor;
bgx |= bgcolor;
}
bit_mask = (1 << ppw) - 1;
eorx = fgx ^ bgx;
k = image->width/ppw;
for (i = 0; i < tablen; ++i)
colortab[i] = (tab[i] & eorx) ^ bgx;
for (i = image->height; i--; ) {
dst = (u32 __iomem *) dst1, shift = 8; src = s;
for (j = k; j--; ) {
shift -= ppw;
end_mask = tab[(*src >> shift) & bit_mask];
FB_WRITEL((end_mask & eorx)^bgx, dst++);
if (!shift) { shift = 8; src++; }
dst = (u32 __iomem *)dst1;
src = s;
switch (ppw) {
case 4: /* 8 bpp */
for (j = k; j; j -= 2, ++src) {
FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++);
}
break;
case 2: /* 16 bpp */
for (j = k; j; j -= 4, ++src) {
FB_WRITEL(colortab[(*src >> 6) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 2) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++);
}
break;
case 1: /* 32 bpp */
for (j = k; j; j -= 8, ++src) {
FB_WRITEL(colortab[(*src >> 7) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 6) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 5) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 4) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 3) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 2) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 1) & bit_mask], dst++);
FB_WRITEL(colortab[(*src >> 0) & bit_mask], dst++);
}
break;
}
dst1 += p->fix.line_length;
s += spitch;
}
}
}
void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
{
u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
@ -292,13 +325,13 @@ void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
} else {
fgcolor = image->fg_color;
bgcolor = image->bg_color;
}
if (32 % bpp == 0 && !start_index && !pitch_index &&
}
if (32 % bpp == 0 && !start_index && !pitch_index &&
((width & (32/bpp-1)) == 0) &&
bpp >= 8 && bpp <= 32)
bpp >= 8 && bpp <= 32)
fast_imageblit(image, p, dst1, fgcolor, bgcolor);
else
else
slow_imageblit(image, p, dst1, fgcolor, bgcolor,
start_index, pitch_index);
} else

View File

@ -50,19 +50,9 @@ bitfill_aligned(struct fb_info *p, unsigned long *dst, int dst_idx,
/* Main chunk */
n /= bits;
while (n >= 8) {
*dst++ = pat;
*dst++ = pat;
*dst++ = pat;
*dst++ = pat;
*dst++ = pat;
*dst++ = pat;
*dst++ = pat;
*dst++ = pat;
n -= 8;
}
while (n--)
*dst++ = pat;
memset_l(dst, pat, n);
dst += n;
/* Trailing bits */
if (last)
*dst = comp(pat, *dst, last);

View File

@ -188,23 +188,29 @@ static void fast_imageblit(const struct fb_image *image, struct fb_info *p,
{
u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
u32 bit_mask, end_mask, eorx, shift;
u32 bit_mask, eorx;
const char *s = image->data, *src;
u32 *dst;
const u32 *tab = NULL;
const u32 *tab;
size_t tablen;
u32 colortab[16];
int i, j, k;
switch (bpp) {
case 8:
tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
tablen = 16;
break;
case 16:
tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
tablen = 4;
break;
case 32:
default:
tab = cfb_tab32;
tablen = 2;
break;
default:
return;
}
for (i = ppw-1; i--; ) {
@ -218,19 +224,40 @@ static void fast_imageblit(const struct fb_image *image, struct fb_info *p,
eorx = fgx ^ bgx;
k = image->width/ppw;
for (i = 0; i < tablen; ++i)
colortab[i] = (tab[i] & eorx) ^ bgx;
for (i = image->height; i--; ) {
dst = dst1;
shift = 8;
src = s;
for (j = k; j--; ) {
shift -= ppw;
end_mask = tab[(*src >> shift) & bit_mask];
*dst++ = (end_mask & eorx) ^ bgx;
if (!shift) {
shift = 8;
src++;
switch (ppw) {
case 4: /* 8 bpp */
for (j = k; j; j -= 2, ++src) {
*dst++ = colortab[(*src >> 4) & bit_mask];
*dst++ = colortab[(*src >> 0) & bit_mask];
}
break;
case 2: /* 16 bpp */
for (j = k; j; j -= 4, ++src) {
*dst++ = colortab[(*src >> 6) & bit_mask];
*dst++ = colortab[(*src >> 4) & bit_mask];
*dst++ = colortab[(*src >> 2) & bit_mask];
*dst++ = colortab[(*src >> 0) & bit_mask];
}
break;
case 1: /* 32 bpp */
for (j = k; j; j -= 8, ++src) {
*dst++ = colortab[(*src >> 7) & bit_mask];
*dst++ = colortab[(*src >> 6) & bit_mask];
*dst++ = colortab[(*src >> 5) & bit_mask];
*dst++ = colortab[(*src >> 4) & bit_mask];
*dst++ = colortab[(*src >> 3) & bit_mask];
*dst++ = colortab[(*src >> 2) & bit_mask];
*dst++ = colortab[(*src >> 1) & bit_mask];
*dst++ = colortab[(*src >> 0) & bit_mask];
}
break;
}
dst1 += p->fix.line_length;
s += spitch;

View File

@ -456,7 +456,7 @@ struct drm_panel;
#define DP_FEC_CAPABILITY_1 0x091 /* 2.0 */
/* DP-HDMI2.1 PCON DSC ENCODER SUPPORT */
#define DP_PCON_DSC_ENCODER_CAP_SIZE 0xC /* 0x9E - 0x92 */
#define DP_PCON_DSC_ENCODER_CAP_SIZE 0xD /* 0x92 through 0x9E */
#define DP_PCON_DSC_ENCODER 0x092
# define DP_PCON_DSC_ENCODER_SUPPORTED (1 << 0)
# define DP_PCON_DSC_PPS_ENC_OVERRIDE (1 << 1)
@ -1530,8 +1530,6 @@ u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SI
int lane);
u8 drm_dp_get_adjust_tx_ffe_preset(const u8 link_status[DP_LINK_STATUS_SIZE],
int lane);
u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE],
unsigned int lane);
#define DP_BRANCH_OUI_HEADER_SIZE 0xc
#define DP_RECEIVER_CAP_SIZE 0xf

View File

@ -130,6 +130,14 @@ struct mipi_dbi_dev {
* @dbi: MIPI DBI interface
*/
struct mipi_dbi dbi;
/**
* @driver_private: Driver private data.
* Necessary for drivers with private data since devm_drm_dev_alloc()
* can't allocate structures that embed a structure which then again
* embeds drm_device.
*/
void *driver_private;
};
static inline struct mipi_dbi_dev *drm_to_mipi_dbi_dev(struct drm_device *drm)

View File

@ -98,6 +98,10 @@ struct drm_object_properties {
* Hence atomic drivers should not use drm_object_property_set_value()
* and drm_object_property_get_value() on mutable objects, i.e. those
* without the DRM_MODE_PROP_IMMUTABLE flag set.
*
* For atomic drivers the default value of properties is stored in this
* array, so drm_object_property_get_default_value can be used to
* retrieve it.
*/
uint64_t values[DRM_OBJECT_MAX_PROPERTY];
};
@ -126,6 +130,9 @@ int drm_object_property_set_value(struct drm_mode_object *obj,
int drm_object_property_get_value(struct drm_mode_object *obj,
struct drm_property *property,
uint64_t *value);
int drm_object_property_get_default_value(struct drm_mode_object *obj,
struct drm_property *property,
uint64_t *val);
void drm_object_attach_property(struct drm_mode_object *obj,
struct drm_property *property,

View File

@ -466,6 +466,8 @@ void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags);
int of_get_drm_display_mode(struct device_node *np,
struct drm_display_mode *dmode, u32 *bus_flags,
int index);
int of_get_drm_panel_display_mode(struct device_node *np,
struct drm_display_mode *dmode, u32 *bus_flags);
#else
static inline int of_get_drm_display_mode(struct device_node *np,
struct drm_display_mode *dmode,
@ -473,6 +475,12 @@ static inline int of_get_drm_display_mode(struct device_node *np,
{
return -EINVAL;
}
static inline int of_get_drm_panel_display_mode(struct device_node *np,
struct drm_display_mode *dmode, u32 *bus_flags)
{
return -EINVAL;
}
#endif
void drm_mode_set_name(struct drm_display_mode *mode);