drm-misc-next for 5.1:

UAPI Changes:
  - Addition of the Allwinner tiled format modifier
 
 Cross-subsystem Changes:
 
 Core Changes:
  - dma-buf documentation improvements
  - Removal of now unused fbdev helpers
  - Addition of new drm fbdev helpers
  - Improvements to tinydrm
  - Addition of new drm_fourcc helpers
  - Impromevents to i2c-over-aux to handle I2C_M_STOP
 
 Driver Changes:
  - Add support for the TI DS90C185 LVDS bridge
  - Improvements to the thc63lvdm83d bridge
  - Improvements to sun4i YUV and scaler support
  - Fix to the powerdown sequence of panel-innolux
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCXEhJzAAKCRDj7w1vZxhR
 xWP9AP9bHH411gnoaaaVCmtIc6MVybuJmQsF7Nx5eQ+Ax61LXwD/az8uMtfaSK/M
 t263gSdturb5p1xDdPuyk083PtlvogI=
 =HzD3
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2019-01-23' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for 5.1:

UAPI Changes:
 - Addition of the Allwinner tiled format modifier

Cross-subsystem Changes:

Core Changes:
 - dma-buf documentation improvements
 - Removal of now unused fbdev helpers
 - Addition of new drm fbdev helpers
 - Improvements to tinydrm
 - Addition of new drm_fourcc helpers
 - Impromevents to i2c-over-aux to handle I2C_M_STOP

Driver Changes:
 - Add support for the TI DS90C185 LVDS bridge
 - Improvements to the thc63lvdm83d bridge
 - Improvements to sun4i YUV and scaler support
 - Fix to the powerdown sequence of panel-innolux

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

From: Maxime Ripard <maxime.ripard@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190123110317.h4tovujaydo2bfz2@flea
This commit is contained in:
Dave Airlie 2019-01-24 19:52:46 +10:00
commit f91168f485
39 changed files with 964 additions and 666 deletions

View File

@ -22,13 +22,11 @@ among others.
Required properties:
- compatible: Must be one or more of the following
- "ti,ds90c185" for the TI DS90C185 FPD-Link Serializer
- "lvds-encoder" for a generic LVDS encoder device
- compatible: Must be "lvds-encoder"
When compatible with the generic version, nodes must list the
device-specific version corresponding to the device first
followed by the generic version.
Any encoder compatible with this generic binding, but with additional
properties not listed here, must list a device specific compatible first
followed by this generic compatible.
Required nodes:
@ -44,8 +42,6 @@ Example
lvds-encoder {
compatible = "lvds-encoder";
#address-cells = <1>;
#size-cells = <0>;
ports {
#address-cells = <1>;

View File

@ -10,7 +10,7 @@ Required properties:
Optional properties:
- pwdn-gpios: Power down control GPIO
- powerdown-gpios: Power down control GPIO (the /PWDN pin, active low).
Required nodes:

View File

@ -0,0 +1,55 @@
Texas Instruments FPD-Link (LVDS) Serializer
--------------------------------------------
The DS90C185 and DS90C187 are low-power serializers for portable
battery-powered applications that reduces the size of the RGB
interface between the host GPU and the display.
Required properties:
- compatible: Should be
"ti,ds90c185", "lvds-encoder" for the TI DS90C185 FPD-Link Serializer
"ti,ds90c187", "lvds-encoder" for the TI DS90C187 FPD-Link Serializer
Optional properties:
- powerdown-gpios: Power down control GPIO (the PDB pin, active-low)
Required nodes:
The devices have two video ports. Their connections are modeled using the OF
graph bindings specified in Documentation/devicetree/bindings/graph.txt.
- Video port 0 for parallel input
- Video port 1 for LVDS output
Example
-------
lvds-encoder {
compatible = "ti,ds90c185", "lvds-encoder";
powerdown-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds_enc_in: endpoint {
remote-endpoint = <&lcdc_out_rgb>;
};
};
port@1 {
reg = <1>;
lvds_enc_out: endpoint {
remote-endpoint = <&lvds_panel_in>;
};
};
};
};

View File

@ -82,30 +82,6 @@ events for atomic commits correctly. But fixing these bugs is good anyway.
Contact: Daniel Vetter, respective driver maintainers
Better manual-upload support for atomic
---------------------------------------
This would be especially useful for tinydrm:
- Add a struct drm_rect dirty_clip to drm_crtc_state. When duplicating the
crtc state, clear that to the max values, x/y = 0 and w/h = MAX_INT, in
__drm_atomic_helper_crtc_duplicate_state().
- Move tinydrm_merge_clips into drm_framebuffer.c, dropping the tinydrm\_
prefix ofc and using drm_fb\_. drm_framebuffer.c makes sense since this
is a function useful to implement the fb->dirty function.
- Create a new drm_fb_dirty function which does essentially what e.g.
mipi_dbi_fb_dirty does. You can use e.g. drm_atomic_helper_update_plane as the
template. But instead of doing a simple full-screen plane update, this new
helper also sets crtc_state->dirty_clip to the right coordinates. And of
course it needs to check whether the fb is actually active (and maybe where),
so there's some book-keeping involved. There's also some good fun involved in
scaling things appropriately. For that case we might simply give up and
declare the entire area covered by the plane as dirty.
Contact: Noralf Trønnes, Daniel Vetter
Fallout from atomic KMS
-----------------------
@ -459,21 +435,10 @@ those drivers as simple as possible, so lots of room for refactoring:
one of the ideas for having a shared dsi/dbi helper, abstracting away the
transport details more.
- tinydrm_gem_cma_prime_import_sg_table should probably go into the cma
helpers, as a _vmapped variant (since not every driver needs the vmap).
And tinydrm_gem_cma_free_object could the be merged into
drm_gem_cma_free_object().
- tinydrm_fb_create we could move into drm_simple_pipe, only need to add
the fb_create hook to drm_simple_pipe_funcs, which would again simplify a
bunch of things (since it gives you a one-stop vfunc for simple drivers).
- Quick aside: The unregister devm stuff is kinda getting the lifetimes of
a drm_device wrong. Doesn't matter, since everyone else gets it wrong
too :-)
- also rework the drm_framebuffer_funcs->dirty hook wire-up, see above.
Contact: Noralf Trønnes, Daniel Vetter
AMD DC Display Driver

View File

@ -170,10 +170,6 @@ config DRM_KMS_CMA_HELPER
bool
depends on DRM
select DRM_GEM_CMA_HELPER
select DRM_KMS_FB_HELPER
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
help
Choose this if you need the KMS CMA helper functions

View File

@ -11,11 +11,13 @@
#include <drm/drm_bridge.h>
#include <drm/drm_panel.h>
#include <linux/gpio/consumer.h>
#include <linux/of_graph.h>
struct lvds_encoder {
struct drm_bridge bridge;
struct drm_bridge *panel_bridge;
struct gpio_desc *powerdown_gpio;
};
static int lvds_encoder_attach(struct drm_bridge *bridge)
@ -28,54 +30,85 @@ static int lvds_encoder_attach(struct drm_bridge *bridge)
bridge);
}
static void lvds_encoder_enable(struct drm_bridge *bridge)
{
struct lvds_encoder *lvds_encoder = container_of(bridge,
struct lvds_encoder,
bridge);
if (lvds_encoder->powerdown_gpio)
gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 0);
}
static void lvds_encoder_disable(struct drm_bridge *bridge)
{
struct lvds_encoder *lvds_encoder = container_of(bridge,
struct lvds_encoder,
bridge);
if (lvds_encoder->powerdown_gpio)
gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 1);
}
static struct drm_bridge_funcs funcs = {
.attach = lvds_encoder_attach,
.enable = lvds_encoder_enable,
.disable = lvds_encoder_disable,
};
static int lvds_encoder_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *port;
struct device_node *endpoint;
struct device_node *panel_node;
struct drm_panel *panel;
struct lvds_encoder *lvds_encoder;
lvds_encoder = devm_kzalloc(&pdev->dev, sizeof(*lvds_encoder),
GFP_KERNEL);
lvds_encoder = devm_kzalloc(dev, sizeof(*lvds_encoder), GFP_KERNEL);
if (!lvds_encoder)
return -ENOMEM;
lvds_encoder->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown",
GPIOD_OUT_HIGH);
if (IS_ERR(lvds_encoder->powerdown_gpio)) {
int err = PTR_ERR(lvds_encoder->powerdown_gpio);
if (err != -EPROBE_DEFER)
dev_err(dev, "powerdown GPIO failure: %d\n", err);
return err;
}
/* Locate the panel DT node. */
port = of_graph_get_port_by_id(pdev->dev.of_node, 1);
port = of_graph_get_port_by_id(dev->of_node, 1);
if (!port) {
dev_dbg(&pdev->dev, "port 1 not found\n");
dev_dbg(dev, "port 1 not found\n");
return -ENXIO;
}
endpoint = of_get_child_by_name(port, "endpoint");
of_node_put(port);
if (!endpoint) {
dev_dbg(&pdev->dev, "no endpoint for port 1\n");
dev_dbg(dev, "no endpoint for port 1\n");
return -ENXIO;
}
panel_node = of_graph_get_remote_port_parent(endpoint);
of_node_put(endpoint);
if (!panel_node) {
dev_dbg(&pdev->dev, "no remote endpoint for port 1\n");
dev_dbg(dev, "no remote endpoint for port 1\n");
return -ENXIO;
}
panel = of_drm_find_panel(panel_node);
of_node_put(panel_node);
if (IS_ERR(panel)) {
dev_dbg(&pdev->dev, "panel not found, deferring probe\n");
dev_dbg(dev, "panel not found, deferring probe\n");
return PTR_ERR(panel);
}
lvds_encoder->panel_bridge =
devm_drm_panel_bridge_add(&pdev->dev,
panel, DRM_MODE_CONNECTOR_LVDS);
devm_drm_panel_bridge_add(dev, panel, DRM_MODE_CONNECTOR_LVDS);
if (IS_ERR(lvds_encoder->panel_bridge))
return PTR_ERR(lvds_encoder->panel_bridge);
@ -83,7 +116,7 @@ static int lvds_encoder_probe(struct platform_device *pdev)
* but we need a bridge attached to our of_node for our user
* to look up.
*/
lvds_encoder->bridge.of_node = pdev->dev.of_node;
lvds_encoder->bridge.of_node = dev->of_node;
lvds_encoder->bridge.funcs = &funcs;
drm_bridge_add(&lvds_encoder->bridge);

View File

@ -333,3 +333,44 @@ drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,
return ret;
}
EXPORT_SYMBOL(drm_atomic_helper_damage_iter_next);
/**
* drm_atomic_helper_damage_merged - Merged plane damage
* @old_state: Old plane state for validation.
* @state: Plane state from which to iterate the damage clips.
* @rect: Returns the merged damage rectangle
*
* This function merges any valid plane damage clips into one rectangle and
* returns it in @rect.
*
* For details see: drm_atomic_helper_damage_iter_init() and
* drm_atomic_helper_damage_iter_next().
*
* Returns:
* True if there is valid plane damage otherwise false.
*/
bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
struct drm_plane_state *state,
struct drm_rect *rect)
{
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
bool valid = false;
rect->x1 = INT_MAX;
rect->y1 = INT_MAX;
rect->x2 = 0;
rect->y2 = 0;
drm_atomic_helper_damage_iter_init(&iter, old_state, state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
rect->x1 = min(rect->x1, clip.x1);
rect->y1 = min(rect->y1, clip.y1);
rect->x2 = max(rect->x2, clip.x2);
rect->y2 = max(rect->y2, clip.y2);
valid = true;
}
return valid;
}
EXPORT_SYMBOL(drm_atomic_helper_damage_merged);

View File

@ -194,11 +194,11 @@ drm_dp_dump_access(const struct drm_dp_aux *aux,
const char *arrow = request == DP_AUX_NATIVE_READ ? "->" : "<-";
if (ret > 0)
drm_dbg(DRM_UT_DP, "%s: 0x%05x AUX %s (ret=%3d) %*ph\n",
aux->name, offset, arrow, ret, min(ret, 20), buffer);
DRM_DEBUG_DP("%s: 0x%05x AUX %s (ret=%3d) %*ph\n",
aux->name, offset, arrow, ret, min(ret, 20), buffer);
else
drm_dbg(DRM_UT_DP, "%s: 0x%05x AUX %s (ret=%3d)\n",
aux->name, offset, arrow, ret);
DRM_DEBUG_DP("%s: 0x%05x AUX %s (ret=%3d)\n",
aux->name, offset, arrow, ret);
}
/**
@ -887,7 +887,8 @@ static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg,
{
msg->request = (i2c_msg->flags & I2C_M_RD) ?
DP_AUX_I2C_READ : DP_AUX_I2C_WRITE;
msg->request |= DP_AUX_I2C_MOT;
if (!(i2c_msg->flags & I2C_M_STOP))
msg->request |= DP_AUX_I2C_MOT;
}
/*

View File

@ -17,20 +17,13 @@
* GNU General Public License for more details.
*/
#include <drm/drmP.h>
#include <drm/drm_client.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_plane.h>
#include <linux/module.h>
struct drm_fbdev_cma {
struct drm_fb_helper fb_helper;
};
/**
* DOC: framebuffer cma helper functions
*
@ -39,16 +32,8 @@ struct drm_fbdev_cma {
*
* drm_gem_fb_create() is used in the &drm_mode_config_funcs.fb_create
* callback function to create a cma backed framebuffer.
*
* An fbdev framebuffer backed by cma is also available by calling
* drm_fb_cma_fbdev_init(). drm_fb_cma_fbdev_fini() tears it down.
*/
static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper)
{
return container_of(helper, struct drm_fbdev_cma, fb_helper);
}
/**
* drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer
* @fb: The framebuffer
@ -119,121 +104,3 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
return paddr;
}
EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr);
/**
* drm_fb_cma_fbdev_init() - Allocate and initialize fbdev emulation
* @dev: DRM device
* @preferred_bpp: Preferred bits per pixel for the device.
* @dev->mode_config.preferred_depth is used if this is zero.
* @max_conn_count: Maximum number of connectors.
* @dev->mode_config.num_connector is used if this is zero.
*
* Returns:
* Zero on success or negative error code on failure.
*/
int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp,
unsigned int max_conn_count)
{
struct drm_fbdev_cma *fbdev_cma;
/* dev->fb_helper will indirectly point to fbdev_cma after this call */
fbdev_cma = drm_fbdev_cma_init(dev, preferred_bpp, max_conn_count);
return PTR_ERR_OR_ZERO(fbdev_cma);
}
EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init);
/**
* drm_fb_cma_fbdev_fini() - Teardown fbdev emulation
* @dev: DRM device
*/
void drm_fb_cma_fbdev_fini(struct drm_device *dev)
{
if (dev->fb_helper)
drm_fbdev_cma_fini(to_fbdev_cma(dev->fb_helper));
}
EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_fini);
static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
.fb_probe = drm_fb_helper_generic_probe,
};
/**
* drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct
* @dev: DRM device
* @preferred_bpp: Preferred bits per pixel for the device
* @max_conn_count: Maximum number of connectors
*
* Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR.
*/
struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
unsigned int preferred_bpp, unsigned int max_conn_count)
{
struct drm_fbdev_cma *fbdev_cma;
struct drm_fb_helper *fb_helper;
int ret;
fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL);
if (!fbdev_cma)
return ERR_PTR(-ENOMEM);
fb_helper = &fbdev_cma->fb_helper;
ret = drm_client_init(dev, &fb_helper->client, "fbdev", NULL);
if (ret)
goto err_free;
ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_cma_helper_funcs,
preferred_bpp, max_conn_count);
if (ret)
goto err_client_put;
drm_client_add(&fb_helper->client);
return fbdev_cma;
err_client_put:
drm_client_release(&fb_helper->client);
err_free:
kfree(fbdev_cma);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(drm_fbdev_cma_init);
/**
* drm_fbdev_cma_fini() - Free drm_fbdev_cma struct
* @fbdev_cma: The drm_fbdev_cma struct
*/
void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma)
{
drm_fb_helper_unregister_fbi(&fbdev_cma->fb_helper);
/* All resources have now been freed by drm_fbdev_fb_destroy() */
}
EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
/**
* drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode
* @fbdev_cma: The drm_fbdev_cma struct, may be NULL
*
* This function is usually called from the &drm_driver.lastclose callback.
*/
void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
{
if (fbdev_cma)
drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper);
}
EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
/**
* drm_fbdev_cma_hotplug_event() - Poll for hotpulug events
* @fbdev_cma: The drm_fbdev_cma struct, may be NULL
*
* This function is usually called from the &drm_mode_config.output_poll_changed
* callback.
*/
void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma)
{
if (fbdev_cma)
drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper);
}
EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event);

View File

@ -3042,16 +3042,8 @@ static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper)
static void drm_fbdev_release(struct drm_fb_helper *fb_helper)
{
drm_fbdev_cleanup(fb_helper);
/*
* FIXME:
* Remove conditional when all CMA drivers have been moved over to using
* drm_fbdev_generic_setup().
*/
if (fb_helper->client.funcs) {
drm_client_release(&fb_helper->client);
kfree(fb_helper);
}
drm_client_release(&fb_helper->client);
kfree(fb_helper);
}
/*

View File

@ -17,6 +17,7 @@
#include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
@ -136,10 +137,9 @@ EXPORT_SYMBOL(drm_gem_fb_create_handle);
* @mode_cmd: Metadata from the userspace framebuffer creation request
* @funcs: vtable to be used for the new framebuffer object
*
* This can be used to set &drm_framebuffer_funcs for drivers that need the
* &drm_framebuffer_funcs.dirty callback. Use drm_gem_fb_create() if you don't
* need to change &drm_framebuffer_funcs.
* The function does buffer size validation.
* This function can be used to set &drm_framebuffer_funcs for drivers that need
* custom framebuffer callbacks. Use drm_gem_fb_create() if you don't need to
* change &drm_framebuffer_funcs. The function does buffer size validation.
*
* Returns:
* Pointer to a &drm_framebuffer on success or an error pointer on failure.
@ -215,8 +215,8 @@ static const struct drm_framebuffer_funcs drm_gem_fb_funcs = {
*
* If your hardware has special alignment or pitch requirements these should be
* checked before calling this function. The function does buffer size
* validation. Use drm_gem_fb_create_with_funcs() if you need to set
* &drm_framebuffer_funcs.dirty.
* validation. Use drm_gem_fb_create_with_dirty() if you need framebuffer
* flushing.
*
* Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
* The ADDFB2 IOCTL calls into this callback.
@ -233,6 +233,44 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
}
EXPORT_SYMBOL_GPL(drm_gem_fb_create);
static const struct drm_framebuffer_funcs drm_gem_fb_funcs_dirtyfb = {
.destroy = drm_gem_fb_destroy,
.create_handle = drm_gem_fb_create_handle,
.dirty = drm_atomic_helper_dirtyfb,
};
/**
* drm_gem_fb_create_with_dirty() - Helper function for the
* &drm_mode_config_funcs.fb_create callback
* @dev: DRM device
* @file: DRM file that holds the GEM handle(s) backing the framebuffer
* @mode_cmd: Metadata from the userspace framebuffer creation request
*
* This function creates a new framebuffer object described by
* &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
* backing the framebuffer. drm_atomic_helper_dirtyfb() is used for the dirty
* callback giving framebuffer flushing through the atomic machinery. Use
* drm_gem_fb_create() if you don't need the dirty callback.
* The function does buffer size validation.
*
* Drivers should also call drm_plane_enable_fb_damage_clips() on all planes
* to enable userspace to use damage clips also with the ATOMIC IOCTL.
*
* Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
* The ADDFB2 IOCTL calls into this callback.
*
* Returns:
* Pointer to a &drm_framebuffer on success or an error pointer on failure.
*/
struct drm_framebuffer *
drm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
&drm_gem_fb_funcs_dirtyfb);
}
EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty);
/**
* drm_gem_fb_prepare_fb() - Prepare a GEM backed framebuffer
* @plane: Plane

View File

@ -70,18 +70,12 @@ static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)
static int innolux_panel_disable(struct drm_panel *panel)
{
struct innolux_panel *innolux = to_innolux_panel(panel);
int err;
if (!innolux->enabled)
return 0;
backlight_disable(innolux->backlight);
err = mipi_dsi_dcs_set_display_off(innolux->link);
if (err < 0)
DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
err);
innolux->enabled = false;
return 0;
@ -95,6 +89,11 @@ static int innolux_panel_unprepare(struct drm_panel *panel)
if (!innolux->prepared)
return 0;
err = mipi_dsi_dcs_set_display_off(innolux->link);
if (err < 0)
DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
err);
err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
if (err < 0) {
DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",

View File

@ -691,7 +691,7 @@ static int ltdc_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct drm_framebuffer *fb = state->fb;
u32 src_x, src_y, src_w, src_h;
u32 src_w, src_h;
DRM_DEBUG_DRIVER("\n");
@ -699,8 +699,6 @@ static int ltdc_plane_atomic_check(struct drm_plane *plane,
return 0;
/* convert src_ from 16:16 format */
src_x = state->src_x >> 16;
src_y = state->src_y >> 16;
src_w = state->src_w >> 16;
src_h = state->src_h >> 16;

View File

@ -45,28 +45,6 @@ static const u32 sunxi_rgb2yuv_coef[12] = {
0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
};
/*
* These coefficients are taken from the A33 BSP from Allwinner.
*
* The first three values of each row are coded as 13-bit signed fixed-point
* numbers, with 10 bits for the fractional part. The fourth value is a
* constant coded as a 14-bit signed fixed-point number with 4 bits for the
* fractional part.
*
* The values in table order give the following colorspace translation:
* G = 1.164 * Y - 0.391 * U - 0.813 * V + 135
* R = 1.164 * Y + 1.596 * V - 222
* B = 1.164 * Y + 2.018 * U + 276
*
* This seems to be a conversion from Y[16:235] UV[16:240] to RGB[0:255],
* following the BT601 spec.
*/
static const u32 sunxi_bt601_yuv2rgb_coef[12] = {
0x000004a7, 0x00001e6f, 0x00001cbf, 0x00000877,
0x000004a7, 0x00000000, 0x00000662, 0x00003211,
0x000004a7, 0x00000812, 0x00000000, 0x00002eb1,
};
static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine)
{
int i;
@ -245,7 +223,8 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend,
SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN);
/* TODO: Add support for the multi-planar YUV formats */
if (format->num_planes == 1)
if (drm_format_info_is_yuv_packed(format) &&
drm_format_info_is_yuv_sampling_422(format))
val |= SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV422;
else
DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", fmt);

View File

@ -97,6 +97,7 @@ static int sun4i_drv_bind(struct device *dev)
}
drm_mode_config_init(drm);
drm->mode_config.allow_fb_modifiers = true;
ret = component_bind_all(drm->dev, drm);
if (ret) {

View File

@ -10,6 +10,7 @@
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@ -48,10 +49,38 @@ static const u32 sun4i_frontend_horz_coef[64] = {
0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
};
/*
* These coefficients are taken from the A33 BSP from Allwinner.
*
* The first three values of each row are coded as 13-bit signed fixed-point
* numbers, with 10 bits for the fractional part. The fourth value is a
* constant coded as a 14-bit signed fixed-point number with 4 bits for the
* fractional part.
*
* The values in table order give the following colorspace translation:
* G = 1.164 * Y - 0.391 * U - 0.813 * V + 135
* R = 1.164 * Y + 1.596 * V - 222
* B = 1.164 * Y + 2.018 * U + 276
*
* This seems to be a conversion from Y[16:235] UV[16:240] to RGB[0:255],
* following the BT601 spec.
*/
const u32 sunxi_bt601_yuv2rgb_coef[12] = {
0x000004a7, 0x00001e6f, 0x00001cbf, 0x00000877,
0x000004a7, 0x00000000, 0x00000662, 0x00003211,
0x000004a7, 0x00000812, 0x00000000, 0x00002eb1,
};
EXPORT_SYMBOL(sunxi_bt601_yuv2rgb_coef);
static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend)
{
int i;
if (frontend->data->has_coef_access_ctrl)
regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL,
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL);
for (i = 0; i < 32; i++) {
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i),
sun4i_frontend_horz_coef[2 * i]);
@ -67,9 +96,11 @@ static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend)
sun4i_frontend_vert_coef[i]);
}
regmap_update_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL,
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL);
if (frontend->data->has_coef_rdy)
regmap_write_bits(frontend->regs,
SUN4I_FRONTEND_FRM_CTRL_REG,
SUN4I_FRONTEND_FRM_CTRL_COEF_RDY,
SUN4I_FRONTEND_FRM_CTRL_COEF_RDY);
}
int sun4i_frontend_init(struct sun4i_frontend *frontend)
@ -84,31 +115,170 @@ void sun4i_frontend_exit(struct sun4i_frontend *frontend)
}
EXPORT_SYMBOL(sun4i_frontend_exit);
static bool sun4i_frontend_format_chroma_requires_swap(uint32_t fmt)
{
switch (fmt) {
case DRM_FORMAT_YVU411:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YVU444:
return true;
default:
return false;
}
}
static bool sun4i_frontend_format_supports_tiling(uint32_t fmt)
{
switch (fmt) {
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV61:
case DRM_FORMAT_YUV411:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YVU411:
return true;
default:
return false;
}
}
void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend,
struct drm_plane *plane)
{
struct drm_plane_state *state = plane->state;
struct drm_framebuffer *fb = state->fb;
unsigned int strides[3] = {};
dma_addr_t paddr;
bool swap;
if (fb->modifier == DRM_FORMAT_MOD_ALLWINNER_TILED) {
unsigned int width = state->src_w >> 16;
unsigned int offset;
strides[0] = SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[0]);
/*
* The X1 offset is the offset to the bottom-right point in the
* end tile, which is the final pixel (at offset width - 1)
* within the end tile (with a 32-byte mask).
*/
offset = (width - 1) & (32 - 1);
regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF0_REG,
SUN4I_FRONTEND_TB_OFF_X1(offset));
if (fb->format->num_planes > 1) {
strides[1] =
SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[1]);
regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF1_REG,
SUN4I_FRONTEND_TB_OFF_X1(offset));
}
if (fb->format->num_planes > 2) {
strides[2] =
SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[2]);
regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF2_REG,
SUN4I_FRONTEND_TB_OFF_X1(offset));
}
} else {
strides[0] = fb->pitches[0];
if (fb->format->num_planes > 1)
strides[1] = fb->pitches[1];
if (fb->format->num_planes > 2)
strides[2] = fb->pitches[2];
}
/* Set the line width */
DRM_DEBUG_DRIVER("Frontend stride: %d bytes\n", fb->pitches[0]);
regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD0_REG,
fb->pitches[0]);
strides[0]);
if (fb->format->num_planes > 1)
regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD1_REG,
strides[1]);
if (fb->format->num_planes > 2)
regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD2_REG,
strides[2]);
/* Some planar formats require chroma channel swapping by hand. */
swap = sun4i_frontend_format_chroma_requires_swap(fb->format->format);
/* Set the physical address of the buffer in memory */
paddr = drm_fb_cma_get_gem_addr(fb, state, 0);
paddr -= PHYS_OFFSET;
DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
DRM_DEBUG_DRIVER("Setting buffer #0 address to %pad\n", &paddr);
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr);
if (fb->format->num_planes > 1) {
paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 2 : 1);
paddr -= PHYS_OFFSET;
DRM_DEBUG_DRIVER("Setting buffer #1 address to %pad\n", &paddr);
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR1_REG,
paddr);
}
if (fb->format->num_planes > 2) {
paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 1 : 2);
paddr -= PHYS_OFFSET;
DRM_DEBUG_DRIVER("Setting buffer #2 address to %pad\n", &paddr);
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR2_REG,
paddr);
}
}
EXPORT_SYMBOL(sun4i_frontend_update_buffer);
static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val)
static int
sun4i_frontend_drm_format_to_input_fmt(const struct drm_format_info *format,
u32 *val)
{
switch (fmt) {
case DRM_FORMAT_XRGB8888:
if (!format->is_yuv)
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB;
else if (drm_format_info_is_yuv_sampling_411(format))
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV411;
else if (drm_format_info_is_yuv_sampling_420(format))
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV420;
else if (drm_format_info_is_yuv_sampling_422(format))
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV422;
else if (drm_format_info_is_yuv_sampling_444(format))
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV444;
else
return -EINVAL;
return 0;
}
static int
sun4i_frontend_drm_format_to_input_mode(const struct drm_format_info *format,
uint64_t modifier, u32 *val)
{
bool tiled = (modifier == DRM_FORMAT_MOD_ALLWINNER_TILED);
switch (format->num_planes) {
case 1:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED;
return 0;
case 2:
*val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_SEMIPLANAR
: SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_SEMIPLANAR;
return 0;
case 3:
*val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_PLANAR
: SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PLANAR;
return 0;
default:
@ -116,27 +286,57 @@ static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val)
}
}
static int sun4i_frontend_drm_format_to_input_mode(uint32_t fmt, u32 *val)
static int
sun4i_frontend_drm_format_to_input_sequence(const struct drm_format_info *format,
u32 *val)
{
if (drm_format_num_planes(fmt) == 1)
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED;
else
return -EINVAL;
/* Planar formats have an explicit input sequence. */
if (drm_format_info_is_yuv_planar(format)) {
*val = 0;
return 0;
}
return 0;
}
static int sun4i_frontend_drm_format_to_input_sequence(uint32_t fmt, u32 *val)
{
switch (fmt) {
switch (format->format) {
case DRM_FORMAT_BGRX8888:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX;
return 0;
case DRM_FORMAT_NV12:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV;
return 0;
case DRM_FORMAT_NV16:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV;
return 0;
case DRM_FORMAT_NV21:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU;
return 0;
case DRM_FORMAT_NV61:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU;
return 0;
case DRM_FORMAT_UYVY:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UYVY;
return 0;
case DRM_FORMAT_VYUY:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VYUY;
return 0;
case DRM_FORMAT_XRGB8888:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB;
return 0;
case DRM_FORMAT_YUYV:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YUYV;
return 0;
case DRM_FORMAT_YVYU:
*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YVYU;
return 0;
default:
return -EINVAL;
}
@ -160,14 +360,32 @@ static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val)
static const uint32_t sun4i_frontend_formats[] = {
DRM_FORMAT_BGRX8888,
DRM_FORMAT_NV12,
DRM_FORMAT_NV16,
DRM_FORMAT_NV21,
DRM_FORMAT_NV61,
DRM_FORMAT_UYVY,
DRM_FORMAT_VYUY,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_YUV411,
DRM_FORMAT_YUV420,
DRM_FORMAT_YUV422,
DRM_FORMAT_YUV444,
DRM_FORMAT_YUYV,
DRM_FORMAT_YVU411,
DRM_FORMAT_YVU420,
DRM_FORMAT_YVU422,
DRM_FORMAT_YVU444,
DRM_FORMAT_YVYU,
};
bool sun4i_frontend_format_is_supported(uint32_t fmt, uint64_t modifier)
{
unsigned int i;
if (modifier != DRM_FORMAT_MOD_LINEAR)
if (modifier == DRM_FORMAT_MOD_ALLWINNER_TILED)
return sun4i_frontend_format_supports_tiling(fmt);
else if (modifier != DRM_FORMAT_MOD_LINEAR)
return false;
for (i = 0; i < ARRAY_SIZE(sun4i_frontend_formats); i++)
@ -183,9 +401,12 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
{
struct drm_plane_state *state = plane->state;
struct drm_framebuffer *fb = state->fb;
uint32_t format = fb->format->format;
const struct drm_format_info *format = fb->format;
uint64_t modifier = fb->modifier;
u32 out_fmt_val;
u32 in_fmt_val, in_mod_val, in_ps_val;
unsigned int i;
u32 bypass;
int ret;
ret = sun4i_frontend_drm_format_to_input_fmt(format, &in_fmt_val);
@ -194,7 +415,8 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
return ret;
}
ret = sun4i_frontend_drm_format_to_input_mode(format, &in_mod_val);
ret = sun4i_frontend_drm_format_to_input_mode(format, modifier,
&in_mod_val);
if (ret) {
DRM_DEBUG_DRIVER("Invalid input mode\n");
return ret;
@ -216,16 +438,39 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
* I have no idea what this does exactly, but it seems to be
* related to the scaler FIR filter phase parameters.
*/
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, 0x400);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, 0x400);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, 0x400);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, 0x400);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, 0x400);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, 0x400);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG,
frontend->data->ch_phase[0].horzphase);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG,
frontend->data->ch_phase[1].horzphase);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG,
frontend->data->ch_phase[0].vertphase[0]);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG,
frontend->data->ch_phase[1].vertphase[0]);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG,
frontend->data->ch_phase[0].vertphase[1]);
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG,
frontend->data->ch_phase[1].vertphase[1]);
/*
* Checking the input format is sufficient since we currently only
* support RGB output formats to the backend. If YUV output formats
* ever get supported, an YUV input and output would require bypassing
* the CSC engine too.
*/
if (format->is_yuv) {
/* Setup the CSC engine for YUV to RGB conversion. */
bypass = 0;
for (i = 0; i < ARRAY_SIZE(sunxi_bt601_yuv2rgb_coef); i++)
regmap_write(frontend->regs,
SUN4I_FRONTEND_CSC_COEF_REG(i),
sunxi_bt601_yuv2rgb_coef[i]);
} else {
bypass = SUN4I_FRONTEND_BYPASS_CSC_EN;
}
regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG,
SUN4I_FRONTEND_BYPASS_CSC_EN,
SUN4I_FRONTEND_BYPASS_CSC_EN);
SUN4I_FRONTEND_BYPASS_CSC_EN, bypass);
regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG,
in_mod_val | in_fmt_val | in_ps_val);
@ -321,6 +566,10 @@ static int sun4i_frontend_bind(struct device *dev, struct device *master,
frontend->dev = dev;
frontend->node = dev->of_node;
frontend->data = of_device_get_match_data(dev);
if (!frontend->data)
return -ENODEV;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
@ -433,8 +682,47 @@ static const struct dev_pm_ops sun4i_frontend_pm_ops = {
.runtime_suspend = sun4i_frontend_runtime_suspend,
};
static const struct sun4i_frontend_data sun4i_a10_frontend = {
.ch_phase = {
{
.horzphase = 0,
.vertphase = { 0, 0 },
},
{
.horzphase = 0xfc000,
.vertphase = { 0xfc000, 0xfc000 },
},
},
.has_coef_rdy = true,
};
static const struct sun4i_frontend_data sun8i_a33_frontend = {
.ch_phase = {
{
.horzphase = 0x400,
.vertphase = { 0x400, 0x400 },
},
{
.horzphase = 0x400,
.vertphase = { 0x400, 0x400 },
},
},
.has_coef_access_ctrl = true,
};
const struct of_device_id sun4i_frontend_of_table[] = {
{ .compatible = "allwinner,sun8i-a33-display-frontend" },
{
.compatible = "allwinner,sun4i-a10-display-frontend",
.data = &sun4i_a10_frontend
},
{
.compatible = "allwinner,sun7i-a20-display-frontend",
.data = &sun4i_a10_frontend
},
{
.compatible = "allwinner,sun8i-a33-display-frontend",
.data = &sun8i_a33_frontend
},
{ }
};
EXPORT_SYMBOL(sun4i_frontend_of_table);

View File

@ -22,12 +22,49 @@
#define SUN4I_FRONTEND_BYPASS_CSC_EN BIT(1)
#define SUN4I_FRONTEND_BUF_ADDR0_REG 0x020
#define SUN4I_FRONTEND_BUF_ADDR1_REG 0x024
#define SUN4I_FRONTEND_BUF_ADDR2_REG 0x028
#define SUN4I_FRONTEND_TB_OFF0_REG 0x030
#define SUN4I_FRONTEND_TB_OFF1_REG 0x034
#define SUN4I_FRONTEND_TB_OFF2_REG 0x038
#define SUN4I_FRONTEND_TB_OFF_X1(x1) ((x1) << 16)
#define SUN4I_FRONTEND_TB_OFF_Y0(y0) ((y0) << 8)
#define SUN4I_FRONTEND_TB_OFF_X0(x0) (x0)
#define SUN4I_FRONTEND_LINESTRD0_REG 0x040
#define SUN4I_FRONTEND_LINESTRD1_REG 0x044
#define SUN4I_FRONTEND_LINESTRD2_REG 0x048
/*
* In tiled mode, the stride is defined as the distance between the start of the
* end line of the current tile and the start of the first line in the next
* vertical tile.
*
* Tiles are represented in row-major order, thus the end line of current tile
* starts at: 31 * 32 (31 lines of 32 cols), the next vertical tile starts at:
* 32-bit-aligned-width * 32 and the distance is:
* 32 * (32-bit-aligned-width - 31).
*/
#define SUN4I_FRONTEND_LINESTRD_TILED(stride) (((stride) - 31) * 32)
#define SUN4I_FRONTEND_INPUT_FMT_REG 0x04c
#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PLANAR (0 << 8)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED (1 << 8)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_SEMIPLANAR (2 << 8)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_PLANAR (4 << 8)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_SEMIPLANAR (6 << 8)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV444 (0 << 4)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV422 (1 << 4)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV420 (2 << 4)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV411 (3 << 4)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB (5 << 4)
#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UYVY 0
#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YUYV 1
#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VYUY 2
#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YVYU 3
#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV 0
#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU 1
#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX 0
#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB 1
@ -35,6 +72,8 @@
#define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_BGRX8888 1
#define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_XRGB8888 2
#define SUN4I_FRONTEND_CSC_COEF_REG(c) (0x070 + (0x4 * (c)))
#define SUN4I_FRONTEND_CH0_INSIZE_REG 0x100
#define SUN4I_FRONTEND_INSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1)))
@ -73,6 +112,16 @@ struct drm_plane;
struct regmap;
struct reset_control;
struct sun4i_frontend_data {
bool has_coef_access_ctrl;
bool has_coef_rdy;
struct {
u32 horzphase;
u32 vertphase[2];
} ch_phase[2];
};
struct sun4i_frontend {
struct list_head list;
struct device *dev;
@ -83,9 +132,12 @@ struct sun4i_frontend {
struct clk *ram_clk;
struct regmap *regs;
struct reset_control *reset;
const struct sun4i_frontend_data *data;
};
extern const struct of_device_id sun4i_frontend_of_table[];
extern const u32 sunxi_bt601_yuv2rgb_coef[12];
int sun4i_frontend_init(struct sun4i_frontend *frontend);
void sun4i_frontend_exit(struct sun4i_frontend *frontend);

View File

@ -114,6 +114,13 @@ static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
sun4i_backend_layer_enable(backend, layer->id, true);
}
static bool sun4i_layer_format_mod_supported(struct drm_plane *plane,
uint32_t format, uint64_t modifier)
{
return sun4i_backend_format_is_supported(format, modifier) ||
sun4i_frontend_format_is_supported(format, modifier);
}
static const struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
.prepare_fb = drm_gem_fb_prepare_fb,
.atomic_disable = sun4i_backend_layer_atomic_disable,
@ -127,6 +134,7 @@ static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
.disable_plane = drm_atomic_helper_disable_plane,
.reset = sun4i_backend_layer_reset,
.update_plane = drm_atomic_helper_update_plane,
.format_mod_supported = sun4i_layer_format_mod_supported,
};
static const uint32_t sun4i_layer_formats[] = {
@ -138,13 +146,31 @@ static const uint32_t sun4i_layer_formats[] = {
DRM_FORMAT_RGBA4444,
DRM_FORMAT_RGB888,
DRM_FORMAT_RGB565,
DRM_FORMAT_NV12,
DRM_FORMAT_NV16,
DRM_FORMAT_NV21,
DRM_FORMAT_NV61,
DRM_FORMAT_UYVY,
DRM_FORMAT_VYUY,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_YUV411,
DRM_FORMAT_YUV420,
DRM_FORMAT_YUV422,
DRM_FORMAT_YUV444,
DRM_FORMAT_YUYV,
DRM_FORMAT_YVU411,
DRM_FORMAT_YVU420,
DRM_FORMAT_YVU422,
DRM_FORMAT_YVU444,
DRM_FORMAT_YVYU,
};
static const uint64_t sun4i_layer_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_ALLWINNER_TILED,
DRM_FORMAT_MOD_INVALID
};
static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
struct sun4i_backend *backend,
enum drm_plane_type type)
@ -161,7 +187,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
&sun4i_backend_layer_funcs,
sun4i_layer_formats,
ARRAY_SIZE(sun4i_layer_formats),
NULL, type, NULL);
sun4i_layer_modifiers, type, NULL);
if (ret) {
dev_err(drm->dev, "Couldn't initialize layer\n");
return ERR_PTR(ret);

View File

@ -39,31 +39,17 @@
* and registers the DRM device using devm_tinydrm_register().
*/
static struct drm_framebuffer *
tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct tinydrm_device *tdev = drm->dev_private;
return drm_gem_fb_create_with_funcs(drm, file_priv, mode_cmd,
tdev->fb_funcs);
}
static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
.fb_create = tinydrm_fb_create,
.fb_create = drm_gem_fb_create_with_dirty,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
const struct drm_framebuffer_funcs *fb_funcs,
struct drm_driver *driver)
{
struct drm_device *drm;
mutex_init(&tdev->dirty_lock);
tdev->fb_funcs = fb_funcs;
/*
* We don't embed drm_device, because that prevent us from using
* devm_kzalloc() to allocate tinydrm_device in the driver since
@ -86,7 +72,6 @@ static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
static void tinydrm_fini(struct tinydrm_device *tdev)
{
drm_mode_config_cleanup(tdev->drm);
mutex_destroy(&tdev->dirty_lock);
tdev->drm->dev_private = NULL;
drm_dev_put(tdev->drm);
}
@ -100,7 +85,6 @@ static void devm_tinydrm_release(void *data)
* devm_tinydrm_init - Initialize tinydrm device
* @parent: Parent device object
* @tdev: tinydrm device
* @fb_funcs: Framebuffer functions
* @driver: DRM driver
*
* This function initializes @tdev, the underlying DRM device and it's
@ -111,12 +95,11 @@ static void devm_tinydrm_release(void *data)
* Zero on success, negative error code on failure.
*/
int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
const struct drm_framebuffer_funcs *fb_funcs,
struct drm_driver *driver)
{
int ret;
ret = tinydrm_init(parent, tdev, fb_funcs, driver);
ret = tinydrm_init(parent, tdev, driver);
if (ret)
return ret;

View File

@ -17,103 +17,15 @@
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_print.h>
#include <drm/tinydrm/tinydrm.h>
#include <drm/drm_rect.h>
#include <drm/tinydrm/tinydrm-helpers.h>
#include <uapi/drm/drm.h>
static unsigned int spi_max;
module_param(spi_max, uint, 0400);
MODULE_PARM_DESC(spi_max, "Set a lower SPI max transfer size");
/**
* tinydrm_merge_clips - Merge clip rectangles
* @dst: Destination clip rectangle
* @src: Source clip rectangle(s)
* @num_clips: Number of @src clip rectangles
* @flags: Dirty fb ioctl flags
* @max_width: Maximum width of @dst
* @max_height: Maximum height of @dst
*
* This function merges @src clip rectangle(s) into @dst. If @src is NULL,
* @max_width and @min_width is used to set a full @dst clip rectangle.
*
* Returns:
* true if it's a full clip, false otherwise
*/
bool tinydrm_merge_clips(struct drm_clip_rect *dst,
struct drm_clip_rect *src, unsigned int num_clips,
unsigned int flags, u32 max_width, u32 max_height)
{
unsigned int i;
if (!src || !num_clips) {
dst->x1 = 0;
dst->x2 = max_width;
dst->y1 = 0;
dst->y2 = max_height;
return true;
}
dst->x1 = ~0;
dst->y1 = ~0;
dst->x2 = 0;
dst->y2 = 0;
for (i = 0; i < num_clips; i++) {
if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY)
i++;
dst->x1 = min(dst->x1, src[i].x1);
dst->x2 = max(dst->x2, src[i].x2);
dst->y1 = min(dst->y1, src[i].y1);
dst->y2 = max(dst->y2, src[i].y2);
}
if (dst->x2 > max_width || dst->y2 > max_height ||
dst->x1 >= dst->x2 || dst->y1 >= dst->y2) {
DRM_DEBUG_KMS("Illegal clip: x1=%u, x2=%u, y1=%u, y2=%u\n",
dst->x1, dst->x2, dst->y1, dst->y2);
dst->x1 = 0;
dst->y1 = 0;
dst->x2 = max_width;
dst->y2 = max_height;
}
return (dst->x2 - dst->x1) == max_width &&
(dst->y2 - dst->y1) == max_height;
}
EXPORT_SYMBOL(tinydrm_merge_clips);
int tinydrm_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int flags, unsigned int color,
struct drm_clip_rect *clips,
unsigned int num_clips)
{
struct tinydrm_device *tdev = fb->dev->dev_private;
struct drm_plane *plane = &tdev->pipe.plane;
int ret = 0;
drm_modeset_lock(&plane->mutex, NULL);
/* fbdev can flush even when we're not interested */
if (plane->state->fb == fb) {
mutex_lock(&tdev->dirty_lock);
ret = tdev->fb_dirty(fb, file_priv, flags,
color, clips, num_clips);
mutex_unlock(&tdev->dirty_lock);
}
drm_modeset_unlock(&plane->mutex);
if (ret)
dev_err_once(fb->dev->dev,
"Failed to update display %d\n", ret);
return ret;
}
EXPORT_SYMBOL(tinydrm_fb_dirty);
/**
* tinydrm_memcpy - Copy clip buffer
* @dst: Destination buffer
@ -122,7 +34,7 @@ EXPORT_SYMBOL(tinydrm_fb_dirty);
* @clip: Clip rectangle area to copy
*/
void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_clip_rect *clip)
struct drm_rect *clip)
{
unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
unsigned int pitch = fb->pitches[0];
@ -146,7 +58,7 @@ EXPORT_SYMBOL(tinydrm_memcpy);
* @clip: Clip rectangle area to copy
*/
void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_clip_rect *clip)
struct drm_rect *clip)
{
size_t len = (clip->x2 - clip->x1) * sizeof(u16);
unsigned int x, y;
@ -186,7 +98,7 @@ EXPORT_SYMBOL(tinydrm_swab16);
*/
void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr,
struct drm_framebuffer *fb,
struct drm_clip_rect *clip, bool swap)
struct drm_rect *clip, bool swap)
{
size_t len = (clip->x2 - clip->x1) * sizeof(u32);
unsigned int x, y;
@ -235,7 +147,7 @@ EXPORT_SYMBOL(tinydrm_xrgb8888_to_rgb565);
* ITU BT.601 is used for the RGB -> luma (brightness) conversion.
*/
void tinydrm_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_clip_rect *clip)
struct drm_rect *clip)
{
unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
unsigned int x, y;

View File

@ -13,7 +13,6 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_modes.h>
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <drm/tinydrm/tinydrm.h>
struct tinydrm_connector {
@ -111,36 +110,6 @@ tinydrm_connector_create(struct drm_device *drm,
return connector;
}
/**
* tinydrm_display_pipe_update - Display pipe update helper
* @pipe: Simple display pipe
* @old_state: Old plane state
*
* This function does a full framebuffer flush if the plane framebuffer
* has changed. It also handles vblank events. Drivers can use this as their
* &drm_simple_display_pipe_funcs->update callback.
*/
void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
struct drm_framebuffer *fb = pipe->plane.state->fb;
struct drm_crtc *crtc = &tdev->pipe.crtc;
if (fb && (fb != old_state->fb)) {
if (tdev->fb_dirty)
tdev->fb_dirty(fb, NULL, 0, 0, NULL, 0);
}
if (crtc->state->event) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
EXPORT_SYMBOL(tinydrm_display_pipe_update);
static int tinydrm_rotate_mode(struct drm_display_mode *mode,
unsigned int rotation)
{

View File

@ -176,7 +176,7 @@ out_enable:
static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
.enable = yx240qv29_enable,
.disable = mipi_dbi_pipe_disable,
.update = tinydrm_display_pipe_update,
.update = mipi_dbi_pipe_update,
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
};

View File

@ -20,11 +20,14 @@
#include <linux/spi/spi.h>
#include <video/mipi_display.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_rect.h>
#include <drm/drm_vblank.h>
#include <drm/tinydrm/mipi-dbi.h>
#include <drm/tinydrm/tinydrm-helpers.h>
@ -75,16 +78,14 @@ static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data)
return mipi_dbi_command_buf(mipi, cmd, par, 2);
}
static int ili9225_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv, unsigned int flags,
unsigned int color, struct drm_clip_rect *clips,
unsigned int num_clips)
static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
{
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
struct tinydrm_device *tdev = fb->dev->dev_private;
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
unsigned int height = rect->y2 - rect->y1;
unsigned int width = rect->x2 - rect->x1;
bool swap = mipi->swap_bytes;
struct drm_clip_rect clip;
u16 x_start, y_start;
u16 x1, x2, y1, y2;
int ret = 0;
@ -92,54 +93,52 @@ static int ili9225_fb_dirty(struct drm_framebuffer *fb,
void *tr;
if (!mipi->enabled)
return 0;
return;
full = tinydrm_merge_clips(&clip, clips, num_clips, flags,
fb->width, fb->height);
full = width == fb->width && height == fb->height;
DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id,
clip.x1, clip.x2, clip.y1, clip.y2);
DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
if (!mipi->dc || !full || swap ||
fb->format->format == DRM_FORMAT_XRGB8888) {
tr = mipi->tx_buf;
ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap);
ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, rect, swap);
if (ret)
return ret;
goto err_msg;
} else {
tr = cma_obj->vaddr;
}
switch (mipi->rotation) {
default:
x1 = clip.x1;
x2 = clip.x2 - 1;
y1 = clip.y1;
y2 = clip.y2 - 1;
x1 = rect->x1;
x2 = rect->x2 - 1;
y1 = rect->y1;
y2 = rect->y2 - 1;
x_start = x1;
y_start = y1;
break;
case 90:
x1 = clip.y1;
x2 = clip.y2 - 1;
y1 = fb->width - clip.x2;
y2 = fb->width - clip.x1 - 1;
x1 = rect->y1;
x2 = rect->y2 - 1;
y1 = fb->width - rect->x2;
y2 = fb->width - rect->x1 - 1;
x_start = x1;
y_start = y2;
break;
case 180:
x1 = fb->width - clip.x2;
x2 = fb->width - clip.x1 - 1;
y1 = fb->height - clip.y2;
y2 = fb->height - clip.y1 - 1;
x1 = fb->width - rect->x2;
x2 = fb->width - rect->x1 - 1;
y1 = fb->height - rect->y2;
y2 = fb->height - rect->y1 - 1;
x_start = x2;
y_start = y2;
break;
case 270:
x1 = fb->height - clip.y2;
x2 = fb->height - clip.y1 - 1;
y1 = clip.x1;
y2 = clip.x2 - 1;
x1 = fb->height - rect->y2;
x2 = fb->height - rect->y1 - 1;
y1 = rect->x1;
y2 = rect->x2 - 1;
x_start = x2;
y_start = y1;
break;
@ -154,16 +153,29 @@ static int ili9225_fb_dirty(struct drm_framebuffer *fb,
ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, y_start);
ret = mipi_dbi_command_buf(mipi, ILI9225_WRITE_DATA_TO_GRAM, tr,
(clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2);
return ret;
width * height * 2);
err_msg:
if (ret)
dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
}
static const struct drm_framebuffer_funcs ili9225_fb_funcs = {
.destroy = drm_gem_fb_destroy,
.create_handle = drm_gem_fb_create_handle,
.dirty = tinydrm_fb_dirty,
};
static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct drm_plane_state *state = pipe->plane.state;
struct drm_crtc *crtc = &pipe->crtc;
struct drm_rect rect;
if (drm_atomic_helper_damage_merged(old_state, state, &rect))
ili9225_fb_dirty(state->fb, &rect);
if (crtc->state->event) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
@ -171,7 +183,14 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
{
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
struct drm_framebuffer *fb = plane_state->fb;
struct device *dev = tdev->drm->dev;
struct drm_rect rect = {
.x1 = 0,
.x2 = fb->width,
.y1 = 0,
.y2 = fb->height,
};
int ret;
u8 am_id;
@ -259,7 +278,8 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x1017);
mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
mipi->enabled = true;
ili9225_fb_dirty(fb, &rect);
}
static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
@ -304,59 +324,10 @@ static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
return tinydrm_spi_transfer(spi, speed_hz, NULL, bpw, par, num);
}
static const u32 ili9225_formats[] = {
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
};
static int ili9225_init(struct device *dev, struct mipi_dbi *mipi,
const struct drm_simple_display_pipe_funcs *pipe_funcs,
struct drm_driver *driver,
const struct drm_display_mode *mode,
unsigned int rotation)
{
size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
struct tinydrm_device *tdev = &mipi->tinydrm;
int ret;
if (!mipi->command)
return -EINVAL;
mutex_init(&mipi->cmdlock);
mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
if (!mipi->tx_buf)
return -ENOMEM;
ret = devm_tinydrm_init(dev, tdev, &ili9225_fb_funcs, driver);
if (ret)
return ret;
tdev->fb_dirty = ili9225_fb_dirty;
ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
DRM_MODE_CONNECTOR_VIRTUAL,
ili9225_formats,
ARRAY_SIZE(ili9225_formats), mode,
rotation);
if (ret)
return ret;
tdev->drm->mode_config.preferred_depth = 16;
mipi->rotation = rotation;
drm_mode_config_reset(tdev->drm);
DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
tdev->drm->mode_config.preferred_depth, rotation);
return 0;
}
static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
.enable = ili9225_pipe_enable,
.disable = ili9225_pipe_disable,
.update = tinydrm_display_pipe_update,
.update = ili9225_pipe_update,
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
};
@ -423,8 +394,8 @@ static int ili9225_probe(struct spi_device *spi)
/* override the command function set in mipi_dbi_spi_init() */
mipi->command = ili9225_dbi_command;
ret = ili9225_init(&spi->dev, mipi, &ili9225_pipe_funcs,
&ili9225_driver, &ili9225_mode, rotation);
ret = mipi_dbi_init(&spi->dev, mipi, &ili9225_pipe_funcs,
&ili9225_driver, &ili9225_mode, rotation);
if (ret)
return ret;

View File

@ -132,7 +132,7 @@ out_enable:
static const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = {
.enable = yx240qv29_enable,
.disable = mipi_dbi_pipe_disable,
.update = tinydrm_display_pipe_update,
.update = mipi_dbi_pipe_update,
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
};

View File

@ -140,7 +140,7 @@ out_enable:
static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = {
.enable = mi0283qt_enable,
.disable = mipi_dbi_pipe_disable,
.update = tinydrm_display_pipe_update,
.update = mipi_dbi_pipe_update,
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
};

View File

@ -17,14 +17,16 @@
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_rect.h>
#include <drm/tinydrm/mipi-dbi.h>
#include <drm/tinydrm/tinydrm-helpers.h>
#include <uapi/drm/drm.h>
#include <video/mipi_display.h>
#define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */
@ -172,7 +174,7 @@ EXPORT_SYMBOL(mipi_dbi_command_buf);
* Zero on success, negative error code on failure.
*/
int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
struct drm_clip_rect *clip, bool swap)
struct drm_rect *clip, bool swap)
{
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
@ -211,58 +213,75 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
}
EXPORT_SYMBOL(mipi_dbi_buf_copy);
static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int flags, unsigned int color,
struct drm_clip_rect *clips,
unsigned int num_clips)
static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
{
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
struct tinydrm_device *tdev = fb->dev->dev_private;
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
unsigned int height = rect->y2 - rect->y1;
unsigned int width = rect->x2 - rect->x1;
bool swap = mipi->swap_bytes;
struct drm_clip_rect clip;
int ret = 0;
bool full;
void *tr;
if (!mipi->enabled)
return 0;
return;
full = tinydrm_merge_clips(&clip, clips, num_clips, flags,
fb->width, fb->height);
full = width == fb->width && height == fb->height;
DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id,
clip.x1, clip.x2, clip.y1, clip.y2);
DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
if (!mipi->dc || !full || swap ||
fb->format->format == DRM_FORMAT_XRGB8888) {
tr = mipi->tx_buf;
ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap);
ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, rect, swap);
if (ret)
return ret;
goto err_msg;
} else {
tr = cma_obj->vaddr;
}
mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS,
(clip.x1 >> 8) & 0xFF, clip.x1 & 0xFF,
((clip.x2 - 1) >> 8) & 0xFF, (clip.x2 - 1) & 0xFF);
(rect->x1 >> 8) & 0xff, rect->x1 & 0xff,
((rect->x2 - 1) >> 8) & 0xff, (rect->x2 - 1) & 0xff);
mipi_dbi_command(mipi, MIPI_DCS_SET_PAGE_ADDRESS,
(clip.y1 >> 8) & 0xFF, clip.y1 & 0xFF,
((clip.y2 - 1) >> 8) & 0xFF, (clip.y2 - 1) & 0xFF);
(rect->y1 >> 8) & 0xff, rect->y1 & 0xff,
((rect->y2 - 1) >> 8) & 0xff, (rect->y2 - 1) & 0xff);
ret = mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START, tr,
(clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2);
return ret;
width * height * 2);
err_msg:
if (ret)
dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
}
static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = {
.destroy = drm_gem_fb_destroy,
.create_handle = drm_gem_fb_create_handle,
.dirty = tinydrm_fb_dirty,
};
/**
* mipi_dbi_pipe_update - Display pipe update helper
* @pipe: Simple display pipe
* @old_state: Old plane state
*
* This function handles framebuffer flushing and vblank events. Drivers can use
* this as their &drm_simple_display_pipe_funcs->update callback.
*/
void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct drm_plane_state *state = pipe->plane.state;
struct drm_crtc *crtc = &pipe->crtc;
struct drm_rect rect;
if (drm_atomic_helper_damage_merged(old_state, state, &rect))
mipi_dbi_fb_dirty(state->fb, &rect);
if (crtc->state->event) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
EXPORT_SYMBOL(mipi_dbi_pipe_update);
/**
* mipi_dbi_enable_flush - MIPI DBI enable helper
@ -273,18 +292,25 @@ static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = {
* This function sets &mipi_dbi->enabled, flushes the whole framebuffer and
* enables the backlight. Drivers can use this in their
* &drm_simple_display_pipe_funcs->enable callback.
*
* Note: Drivers which don't use mipi_dbi_pipe_update() because they have custom
* framebuffer flushing, can't use this function since they both use the same
* flushing code.
*/
void mipi_dbi_enable_flush(struct mipi_dbi *mipi,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
struct tinydrm_device *tdev = &mipi->tinydrm;
struct drm_framebuffer *fb = plane_state->fb;
struct drm_rect rect = {
.x1 = 0,
.x2 = fb->width,
.y1 = 0,
.y2 = fb->height,
};
mipi->enabled = true;
if (fb)
tdev->fb_dirty(fb, NULL, 0, 0, NULL, 0);
mipi_dbi_fb_dirty(fb, &rect);
backlight_enable(mipi->backlight);
}
EXPORT_SYMBOL(mipi_dbi_enable_flush);
@ -376,12 +402,10 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
if (!mipi->tx_buf)
return -ENOMEM;
ret = devm_tinydrm_init(dev, tdev, &mipi_dbi_fb_funcs, driver);
ret = devm_tinydrm_init(dev, tdev, driver);
if (ret)
return ret;
tdev->fb_dirty = mipi_dbi_fb_dirty;
/* TODO: Maybe add DRM_MODE_CONNECTOR_SPI */
ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
DRM_MODE_CONNECTOR_VIRTUAL,
@ -391,6 +415,8 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
if (ret)
return ret;
drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
tdev->drm->mode_config.preferred_depth = 16;
mipi->rotation = rotation;

View File

@ -26,10 +26,13 @@
#include <linux/spi/spi.h>
#include <linux/thermal.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_rect.h>
#include <drm/drm_vblank.h>
#include <drm/tinydrm/tinydrm.h>
#include <drm/tinydrm/tinydrm-helpers.h>
@ -522,17 +525,13 @@ static void repaper_gray8_to_mono_reversed(u8 *buf, u32 width, u32 height)
}
}
static int repaper_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int flags, unsigned int color,
struct drm_clip_rect *clips,
unsigned int num_clips)
static int repaper_fb_dirty(struct drm_framebuffer *fb)
{
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
struct tinydrm_device *tdev = fb->dev->dev_private;
struct repaper_epd *epd = epd_from_tinydrm(tdev);
struct drm_clip_rect clip;
struct drm_rect clip;
u8 *buf = NULL;
int ret = 0;
@ -625,12 +624,6 @@ out_free:
return ret;
}
static const struct drm_framebuffer_funcs repaper_fb_funcs = {
.destroy = drm_gem_fb_destroy,
.create_handle = drm_gem_fb_create_handle,
.dirty = tinydrm_fb_dirty,
};
static void power_off(struct repaper_epd *epd)
{
/* Turn off power and all signals */
@ -794,9 +787,7 @@ static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
DRM_DEBUG_DRIVER("\n");
mutex_lock(&tdev->dirty_lock);
epd->enabled = false;
mutex_unlock(&tdev->dirty_lock);
/* Nothing frame */
for (line = 0; line < epd->height; line++)
@ -839,10 +830,28 @@ static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
power_off(epd);
}
static void repaper_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct drm_plane_state *state = pipe->plane.state;
struct drm_crtc *crtc = &pipe->crtc;
struct drm_rect rect;
if (drm_atomic_helper_damage_merged(old_state, state, &rect))
repaper_fb_dirty(state->fb);
if (crtc->state->event) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = {
.enable = repaper_pipe_enable,
.disable = repaper_pipe_disable,
.update = tinydrm_display_pipe_update,
.update = repaper_pipe_update,
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
};
@ -1056,12 +1065,10 @@ static int repaper_probe(struct spi_device *spi)
tdev = &epd->tinydrm;
ret = devm_tinydrm_init(dev, tdev, &repaper_fb_funcs, &repaper_driver);
ret = devm_tinydrm_init(dev, tdev, &repaper_driver);
if (ret)
return ret;
tdev->fb_dirty = repaper_fb_dirty;
ret = tinydrm_display_pipe_init(tdev, &repaper_pipe_funcs,
DRM_MODE_CONNECTOR_VIRTUAL,
repaper_formats,

View File

@ -17,10 +17,13 @@
#include <linux/spi/spi.h>
#include <video/mipi_display.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_rect.h>
#include <drm/drm_vblank.h>
#include <drm/tinydrm/mipi-dbi.h>
#include <drm/tinydrm/tinydrm-helpers.h>
@ -62,7 +65,7 @@ static const u8 st7586_lookup[] = { 0x7, 0x4, 0x2, 0x0 };
static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr,
struct drm_framebuffer *fb,
struct drm_clip_rect *clip)
struct drm_rect *clip)
{
size_t len = (clip->x2 - clip->x1) * (clip->y2 - clip->y1);
unsigned int x, y;
@ -88,7 +91,7 @@ static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr,
}
static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb,
struct drm_clip_rect *clip)
struct drm_rect *clip)
{
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
@ -111,57 +114,62 @@ static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb,
return ret;
}
static int st7586_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv, unsigned int flags,
unsigned int color, struct drm_clip_rect *clips,
unsigned int num_clips)
static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
{
struct tinydrm_device *tdev = fb->dev->dev_private;
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
struct drm_clip_rect clip;
int start, end;
int ret = 0;
if (!mipi->enabled)
return 0;
tinydrm_merge_clips(&clip, clips, num_clips, flags, fb->width,
fb->height);
return;
/* 3 pixels per byte, so grow clip to nearest multiple of 3 */
clip.x1 = rounddown(clip.x1, 3);
clip.x2 = roundup(clip.x2, 3);
rect->x1 = rounddown(rect->x1, 3);
rect->x2 = roundup(rect->x2, 3);
DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id,
clip.x1, clip.x2, clip.y1, clip.y2);
DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
ret = st7586_buf_copy(mipi->tx_buf, fb, &clip);
ret = st7586_buf_copy(mipi->tx_buf, fb, rect);
if (ret)
return ret;
goto err_msg;
/* Pixels are packed 3 per byte */
start = clip.x1 / 3;
end = clip.x2 / 3;
start = rect->x1 / 3;
end = rect->x2 / 3;
mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS,
(start >> 8) & 0xFF, start & 0xFF,
(end >> 8) & 0xFF, (end - 1) & 0xFF);
mipi_dbi_command(mipi, MIPI_DCS_SET_PAGE_ADDRESS,
(clip.y1 >> 8) & 0xFF, clip.y1 & 0xFF,
(clip.y2 >> 8) & 0xFF, (clip.y2 - 1) & 0xFF);
(rect->y1 >> 8) & 0xFF, rect->y1 & 0xFF,
(rect->y2 >> 8) & 0xFF, (rect->y2 - 1) & 0xFF);
ret = mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START,
(u8 *)mipi->tx_buf,
(end - start) * (clip.y2 - clip.y1));
return ret;
(end - start) * (rect->y2 - rect->y1));
err_msg:
if (ret)
dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
}
static const struct drm_framebuffer_funcs st7586_fb_funcs = {
.destroy = drm_gem_fb_destroy,
.create_handle = drm_gem_fb_create_handle,
.dirty = tinydrm_fb_dirty,
};
static void st7586_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct drm_plane_state *state = pipe->plane.state;
struct drm_crtc *crtc = &pipe->crtc;
struct drm_rect rect;
if (drm_atomic_helper_damage_merged(old_state, state, &rect))
st7586_fb_dirty(state->fb, &rect);
if (crtc->state->event) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
@ -169,6 +177,13 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
{
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
struct drm_framebuffer *fb = plane_state->fb;
struct drm_rect rect = {
.x1 = 0,
.x2 = fb->width,
.y1 = 0,
.y2 = fb->height,
};
int ret;
u8 addr_mode;
@ -225,9 +240,10 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
msleep(100);
mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
mipi->enabled = true;
st7586_fb_dirty(fb, &rect);
mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
}
static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
@ -263,12 +279,10 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
if (!mipi->tx_buf)
return -ENOMEM;
ret = devm_tinydrm_init(dev, tdev, &st7586_fb_funcs, driver);
ret = devm_tinydrm_init(dev, tdev, driver);
if (ret)
return ret;
tdev->fb_dirty = st7586_fb_dirty;
ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
DRM_MODE_CONNECTOR_VIRTUAL,
st7586_formats,
@ -277,6 +291,8 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
if (ret)
return ret;
drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
tdev->drm->mode_config.preferred_depth = 32;
mipi->rotation = rotation;
@ -291,7 +307,7 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
.enable = st7586_pipe_enable,
.disable = st7586_pipe_disable,
.update = tinydrm_display_pipe_update,
.update = st7586_pipe_update,
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
};

View File

@ -106,7 +106,7 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = {
.enable = jd_t18003_t01_pipe_enable,
.disable = mipi_dbi_pipe_disable,
.update = tinydrm_display_pipe_update,
.update = mipi_dbi_pipe_update,
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
};

View File

@ -78,6 +78,9 @@ drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,
bool
drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,
struct drm_rect *rect);
bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
struct drm_plane_state *state,
struct drm_rect *rect);
/**
* drm_helper_get_plane_damage_clips - Returns damage clips in &drm_rect.

View File

@ -2,31 +2,9 @@
#ifndef __DRM_FB_CMA_HELPER_H__
#define __DRM_FB_CMA_HELPER_H__
struct drm_fbdev_cma;
struct drm_gem_cma_object;
struct drm_fb_helper_surface_size;
struct drm_framebuffer_funcs;
struct drm_fb_helper_funcs;
struct drm_framebuffer;
struct drm_fb_helper;
struct drm_device;
struct drm_file;
struct drm_mode_fb_cmd2;
struct drm_plane;
struct drm_plane_state;
int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp,
unsigned int max_conn_count);
void drm_fb_cma_fbdev_fini(struct drm_device *dev);
struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
unsigned int preferred_bpp, unsigned int max_conn_count);
void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma);
void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma);
void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma);
struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
unsigned int plane);

View File

@ -143,6 +143,123 @@ struct drm_format_name_buf {
char str[32];
};
/**
* drm_format_info_is_yuv_packed - check that the format info matches a YUV
* format with data laid in a single plane
* @info: format info
*
* Returns:
* A boolean indicating whether the format info matches a packed YUV format.
*/
static inline bool
drm_format_info_is_yuv_packed(const struct drm_format_info *info)
{
return info->is_yuv && info->num_planes == 1;
}
/**
* drm_format_info_is_yuv_semiplanar - check that the format info matches a YUV
* format with data laid in two planes (luminance and chrominance)
* @info: format info
*
* Returns:
* A boolean indicating whether the format info matches a semiplanar YUV format.
*/
static inline bool
drm_format_info_is_yuv_semiplanar(const struct drm_format_info *info)
{
return info->is_yuv && info->num_planes == 2;
}
/**
* drm_format_info_is_yuv_planar - check that the format info matches a YUV
* format with data laid in three planes (one for each YUV component)
* @info: format info
*
* Returns:
* A boolean indicating whether the format info matches a planar YUV format.
*/
static inline bool
drm_format_info_is_yuv_planar(const struct drm_format_info *info)
{
return info->is_yuv && info->num_planes == 3;
}
/**
* drm_format_info_is_yuv_sampling_410 - check that the format info matches a
* YUV format with 4:1:0 sub-sampling
* @info: format info
*
* Returns:
* A boolean indicating whether the format info matches a YUV format with 4:1:0
* sub-sampling.
*/
static inline bool
drm_format_info_is_yuv_sampling_410(const struct drm_format_info *info)
{
return info->is_yuv && info->hsub == 4 && info->vsub == 4;
}
/**
* drm_format_info_is_yuv_sampling_411 - check that the format info matches a
* YUV format with 4:1:1 sub-sampling
* @info: format info
*
* Returns:
* A boolean indicating whether the format info matches a YUV format with 4:1:1
* sub-sampling.
*/
static inline bool
drm_format_info_is_yuv_sampling_411(const struct drm_format_info *info)
{
return info->is_yuv && info->hsub == 4 && info->vsub == 1;
}
/**
* drm_format_info_is_yuv_sampling_420 - check that the format info matches a
* YUV format with 4:2:0 sub-sampling
* @info: format info
*
* Returns:
* A boolean indicating whether the format info matches a YUV format with 4:2:0
* sub-sampling.
*/
static inline bool
drm_format_info_is_yuv_sampling_420(const struct drm_format_info *info)
{
return info->is_yuv && info->hsub == 2 && info->vsub == 2;
}
/**
* drm_format_info_is_yuv_sampling_422 - check that the format info matches a
* YUV format with 4:2:2 sub-sampling
* @info: format info
*
* Returns:
* A boolean indicating whether the format info matches a YUV format with 4:2:2
* sub-sampling.
*/
static inline bool
drm_format_info_is_yuv_sampling_422(const struct drm_format_info *info)
{
return info->is_yuv && info->hsub == 2 && info->vsub == 1;
}
/**
* drm_format_info_is_yuv_sampling_444 - check that the format info matches a
* YUV format with 4:4:4 sub-sampling
* @info: format info
*
* Returns:
* A boolean indicating whether the format info matches a YUV format with 4:4:4
* sub-sampling.
*/
static inline bool
drm_format_info_is_yuv_sampling_444(const struct drm_format_info *info)
{
return info->is_yuv && info->hsub == 1 && info->vsub == 1;
}
const struct drm_format_info *__drm_format_info(u32 format);
const struct drm_format_info *drm_format_info(u32 format);
const struct drm_format_info *

View File

@ -25,6 +25,9 @@ drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
struct drm_framebuffer *
drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
const struct drm_mode_fb_cmd2 *mode_cmd);
struct drm_framebuffer *
drm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file,
const struct drm_mode_fb_cmd2 *mode_cmd);
int drm_gem_fb_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *state);

View File

@ -71,7 +71,7 @@
* FIXME: All users of drm_can_sleep should be removed (see todo.rst)
*
* Returns:
* True if kgdb is active or we are in an atomic context or irqs are disabled
* False if kgdb is active, we are in atomic context or irqs are disabled.
*/
static inline bool drm_can_sleep(void)
{

View File

@ -14,6 +14,7 @@
#include <drm/tinydrm/tinydrm.h>
struct drm_rect;
struct spi_device;
struct gpio_desc;
struct regulator;
@ -67,6 +68,8 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
const struct drm_simple_display_pipe_funcs *pipe_funcs,
struct drm_driver *driver,
const struct drm_display_mode *mode, unsigned int rotation);
void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state);
void mipi_dbi_enable_flush(struct mipi_dbi *mipi,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plan_state);
@ -80,7 +83,7 @@ u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len);
int mipi_dbi_command_read(struct mipi_dbi *mipi, u8 cmd, u8 *val);
int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len);
int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
struct drm_clip_rect *clip, bool swap);
struct drm_rect *clip, bool swap);
/**
* mipi_dbi_command - MIPI DCS command with optional parameter(s)
* @mipi: MIPI structure

View File

@ -11,8 +11,8 @@
#define __LINUX_TINYDRM_HELPERS_H
struct backlight_device;
struct tinydrm_device;
struct drm_clip_rect;
struct drm_framebuffer;
struct drm_rect;
struct spi_transfer;
struct spi_message;
struct spi_device;
@ -33,23 +33,15 @@ static inline bool tinydrm_machine_little_endian(void)
#endif
}
bool tinydrm_merge_clips(struct drm_clip_rect *dst,
struct drm_clip_rect *src, unsigned int num_clips,
unsigned int flags, u32 max_width, u32 max_height);
int tinydrm_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int flags, unsigned int color,
struct drm_clip_rect *clips,
unsigned int num_clips);
void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_clip_rect *clip);
struct drm_rect *clip);
void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_clip_rect *clip);
struct drm_rect *clip);
void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr,
struct drm_framebuffer *fb,
struct drm_clip_rect *clip, bool swap);
struct drm_rect *clip, bool swap);
void tinydrm_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_clip_rect *clip);
struct drm_rect *clip);
size_t tinydrm_spi_max_transfer_size(struct spi_device *spi, size_t max_len);
bool tinydrm_spi_bpw_supported(struct spi_device *spi, u8 bpw);

View File

@ -10,14 +10,9 @@
#ifndef __LINUX_TINYDRM_H
#define __LINUX_TINYDRM_H
#include <linux/mutex.h>
#include <drm/drm_simple_kms_helper.h>
struct drm_clip_rect;
struct drm_driver;
struct drm_file;
struct drm_framebuffer;
struct drm_framebuffer_funcs;
/**
* struct tinydrm_device - tinydrm device
@ -32,24 +27,6 @@ struct tinydrm_device {
* @pipe: Display pipe structure
*/
struct drm_simple_display_pipe pipe;
/**
* @dirty_lock: Serializes framebuffer flushing
*/
struct mutex dirty_lock;
/**
* @fb_funcs: Framebuffer functions used when creating framebuffers
*/
const struct drm_framebuffer_funcs *fb_funcs;
/**
* @fb_dirty: Framebuffer dirty callback
*/
int (*fb_dirty)(struct drm_framebuffer *framebuffer,
struct drm_file *file_priv, unsigned flags,
unsigned color, struct drm_clip_rect *clips,
unsigned num_clips);
};
static inline struct tinydrm_device *
@ -82,13 +59,10 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
.clock = 1 /* pass validation */
int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
const struct drm_framebuffer_funcs *fb_funcs,
struct drm_driver *driver);
int devm_tinydrm_register(struct tinydrm_device *tdev);
void tinydrm_shutdown(struct tinydrm_device *tdev);
void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state);
int
tinydrm_display_pipe_init(struct tinydrm_device *tdev,
const struct drm_simple_display_pipe_funcs *funcs,

View File

@ -40,6 +40,7 @@ struct dma_fence_array_cb {
* @num_fences: number of fences in the array
* @num_pending: fences in the array still pending
* @fences: array of the fences
* @work: internal irq_work function
*/
struct dma_fence_array {
struct dma_fence base;

View File

@ -238,6 +238,8 @@ extern "C" {
#define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06
#define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07
#define DRM_FORMAT_MOD_VENDOR_ARM 0x08
#define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
/* add more to the end as needed */
#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1)
@ -667,6 +669,20 @@ extern "C" {
*/
#define AFBC_FORMAT_MOD_BCH (1ULL << 11)
/*
* Allwinner tiled modifier
*
* This tiling mode is implemented by the VPU found on all Allwinner platforms,
* codenamed sunxi. It is associated with a YUV format that uses either 2 or 3
* planes.
*
* With this tiling, the luminance samples are disposed in tiles representing
* 32x32 pixels and the chrominance samples in tiles representing 32x64 pixels.
* The pixel order in each tile is linear and the tiles are disposed linearly,
* both in row-major order.
*/
#define DRM_FORMAT_MOD_ALLWINNER_TILED fourcc_mod_code(ALLWINNER, 1)
#if defined(__cplusplus)
}
#endif