Merge tag 'drm-tinydrm-2017-02-18' of https://github.com/notro/linux into drm-next
Add tinydrm * tag 'drm-tinydrm-2017-02-18' of https://github.com/notro/linux: drm/tinydrm: Add support for Multi-Inno MI0283QT display dt-bindings: Add Multi-Inno MI0283QT binding dt-bindings: display/panel: Add common rotation property of: Add vendor prefix for Multi-Inno drm/tinydrm: Add MIPI DBI support drm/tinydrm: Add helper functions drm: Add DRM support for tiny LCD displays
This commit is contained in:
commit
a5eb76d9c8
@ -0,0 +1,27 @@
|
||||
Multi-Inno MI0283QT display panel
|
||||
|
||||
Required properties:
|
||||
- compatible: "multi-inno,mi0283qt".
|
||||
|
||||
The node for this driver must be a child node of a SPI controller, hence
|
||||
all mandatory properties described in ../spi/spi-bus.txt must be specified.
|
||||
|
||||
Optional properties:
|
||||
- dc-gpios: D/C pin. The presence/absence of this GPIO determines
|
||||
the panel interface mode (IM[3:0] pins):
|
||||
- present: IM=x110 4-wire 8-bit data serial interface
|
||||
- absent: IM=x101 3-wire 9-bit data serial interface
|
||||
- reset-gpios: Reset pin
|
||||
- power-supply: A regulator node for the supply voltage.
|
||||
- backlight: phandle of the backlight device attached to the panel
|
||||
- rotation: panel rotation in degrees counter clockwise (0,90,180,270)
|
||||
|
||||
Example:
|
||||
mi0283qt@0{
|
||||
compatible = "multi-inno,mi0283qt";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <32000000>;
|
||||
rotation = <90>;
|
||||
dc-gpios = <&gpio 25 0>;
|
||||
backlight = <&backlight>;
|
||||
};
|
@ -0,0 +1,4 @@
|
||||
Common display properties
|
||||
-------------------------
|
||||
|
||||
- rotation: Display rotation in degrees counter clockwise (0,90,180,270)
|
@ -187,6 +187,7 @@ mpl MPL AG
|
||||
mqmaker mqmaker Inc.
|
||||
msi Micro-Star International Co. Ltd.
|
||||
mti Imagination Technologies Ltd. (formerly MIPS Technologies Inc.)
|
||||
multi-inno Multi-Inno Technology Co.,Ltd
|
||||
mundoreader Mundo Reader S.L.
|
||||
murata Murata Manufacturing Co., Ltd.
|
||||
mxicy Macronix International Co., Ltd.
|
||||
|
@ -11,6 +11,7 @@ Linux GPU Driver Developer's Guide
|
||||
drm-kms-helpers
|
||||
drm-uapi
|
||||
i915
|
||||
tinydrm
|
||||
vga-switcheroo
|
||||
vgaarbiter
|
||||
|
||||
|
42
Documentation/gpu/tinydrm.rst
Normal file
42
Documentation/gpu/tinydrm.rst
Normal file
@ -0,0 +1,42 @@
|
||||
==========================
|
||||
drm/tinydrm Driver library
|
||||
==========================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
|
||||
:doc: overview
|
||||
|
||||
Core functionality
|
||||
==================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
|
||||
:doc: core
|
||||
|
||||
.. kernel-doc:: include/drm/tinydrm/tinydrm.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
|
||||
:export:
|
||||
|
||||
Additional helpers
|
||||
==================
|
||||
|
||||
.. kernel-doc:: include/drm/tinydrm/tinydrm-helpers.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
|
||||
:export:
|
||||
|
||||
MIPI DBI Compatible Controllers
|
||||
===============================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/mipi-dbi.c
|
||||
:doc: overview
|
||||
|
||||
.. kernel-doc:: include/drm/tinydrm/mipi-dbi.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/mipi-dbi.c
|
||||
:export:
|
@ -4245,6 +4245,12 @@ S: Supported
|
||||
F: drivers/gpu/drm/mediatek/
|
||||
F: Documentation/devicetree/bindings/display/mediatek/
|
||||
|
||||
DRM DRIVER FOR MI0283QT
|
||||
M: Noralf Trønnes <noralf@tronnes.org>
|
||||
S: Maintained
|
||||
F: drivers/gpu/drm/tinydrm/mi0283qt.c
|
||||
F: Documentation/devicetree/bindings/display/multi-inno,mi0283qt.txt
|
||||
|
||||
DRM DRIVER FOR MSM ADRENO GPU
|
||||
M: Rob Clark <robdclark@gmail.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
|
@ -263,6 +263,8 @@ source "drivers/gpu/drm/mxsfb/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/meson/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/tinydrm/Kconfig"
|
||||
|
||||
# Keep legacy drivers last
|
||||
|
||||
menuconfig DRM_LEGACY
|
||||
|
@ -94,3 +94,4 @@ obj-$(CONFIG_DRM_ARCPGU)+= arc/
|
||||
obj-y += hisilicon/
|
||||
obj-$(CONFIG_DRM_ZTE) += zte/
|
||||
obj-$(CONFIG_DRM_MXSFB) += mxsfb/
|
||||
obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
|
||||
|
21
drivers/gpu/drm/tinydrm/Kconfig
Normal file
21
drivers/gpu/drm/tinydrm/Kconfig
Normal file
@ -0,0 +1,21 @@
|
||||
menuconfig DRM_TINYDRM
|
||||
tristate "Support for simple displays"
|
||||
depends on DRM
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select BACKLIGHT_LCD_SUPPORT
|
||||
select BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Choose this option if you have a tinydrm supported display.
|
||||
If M is selected the module will be called tinydrm.
|
||||
|
||||
config TINYDRM_MIPI_DBI
|
||||
tristate
|
||||
|
||||
config TINYDRM_MI0283QT
|
||||
tristate "DRM support for MI0283QT"
|
||||
depends on DRM_TINYDRM && SPI
|
||||
select TINYDRM_MIPI_DBI
|
||||
help
|
||||
DRM driver for the Multi-Inno MI0283QT display panel
|
||||
If M is selected the module will be called mi0283qt.
|
7
drivers/gpu/drm/tinydrm/Makefile
Normal file
7
drivers/gpu/drm/tinydrm/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
obj-$(CONFIG_DRM_TINYDRM) += core/
|
||||
|
||||
# Controllers
|
||||
obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o
|
||||
|
||||
# Displays
|
||||
obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o
|
3
drivers/gpu/drm/tinydrm/core/Makefile
Normal file
3
drivers/gpu/drm/tinydrm/core/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
tinydrm-y := tinydrm-core.o tinydrm-pipe.o tinydrm-helpers.o
|
||||
|
||||
obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
|
376
drivers/gpu/drm/tinydrm/core/tinydrm-core.c
Normal file
376
drivers/gpu/drm/tinydrm/core/tinydrm-core.c
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Noralf Trønnes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/tinydrm/tinydrm.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-buf.h>
|
||||
|
||||
/**
|
||||
* DOC: overview
|
||||
*
|
||||
* This library provides driver helpers for very simple display hardware.
|
||||
*
|
||||
* It is based on &drm_simple_display_pipe coupled with a &drm_connector which
|
||||
* has only one fixed &drm_display_mode. The framebuffers are backed by the
|
||||
* cma helper and have support for framebuffer flushing (dirty).
|
||||
* fbdev support is also included.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: core
|
||||
*
|
||||
* The driver allocates &tinydrm_device, initializes it using
|
||||
* devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
|
||||
* and registers the DRM device using devm_tinydrm_register().
|
||||
*/
|
||||
|
||||
/**
|
||||
* tinydrm_lastclose - DRM lastclose helper
|
||||
* @drm: DRM device
|
||||
*
|
||||
* This function ensures that fbdev is restored when drm_lastclose() is called
|
||||
* on the last drm_release(). Drivers can use this as their
|
||||
* &drm_driver->lastclose callback.
|
||||
*/
|
||||
void tinydrm_lastclose(struct drm_device *drm)
|
||||
{
|
||||
struct tinydrm_device *tdev = drm->dev_private;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
drm_fbdev_cma_restore_mode(tdev->fbdev_cma);
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_lastclose);
|
||||
|
||||
/**
|
||||
* tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from
|
||||
* another driver's scatter/gather table of pinned pages
|
||||
* @drm: DRM device to import into
|
||||
* @attach: DMA-BUF attachment
|
||||
* @sgt: Scatter/gather table of pinned pages
|
||||
*
|
||||
* This function imports a scatter/gather table exported via DMA-BUF by
|
||||
* another driver using drm_gem_cma_prime_import_sg_table(). It sets the
|
||||
* kernel virtual address on the CMA object. Drivers should use this as their
|
||||
* &drm_driver->gem_prime_import_sg_table callback if they need the virtual
|
||||
* address. tinydrm_gem_cma_free_object() should be used in combination with
|
||||
* this function.
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to a newly created GEM object or an ERR_PTR-encoded negative
|
||||
* error code on failure.
|
||||
*/
|
||||
struct drm_gem_object *
|
||||
tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm,
|
||||
struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt)
|
||||
{
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
struct drm_gem_object *obj;
|
||||
void *vaddr;
|
||||
|
||||
vaddr = dma_buf_vmap(attach->dmabuf);
|
||||
if (!vaddr) {
|
||||
DRM_ERROR("Failed to vmap PRIME buffer\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt);
|
||||
if (IS_ERR(obj)) {
|
||||
dma_buf_vunmap(attach->dmabuf, vaddr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
cma_obj = to_drm_gem_cma_obj(obj);
|
||||
cma_obj->vaddr = vaddr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table);
|
||||
|
||||
/**
|
||||
* tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM
|
||||
* object
|
||||
* @gem_obj: GEM object to free
|
||||
*
|
||||
* This function frees the backing memory of the CMA GEM object, cleans up the
|
||||
* GEM object state and frees the memory used to store the object itself using
|
||||
* drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel
|
||||
* virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers
|
||||
* can use this as their &drm_driver->gem_free_object callback.
|
||||
*/
|
||||
void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj)
|
||||
{
|
||||
if (gem_obj->import_attach) {
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
|
||||
cma_obj = to_drm_gem_cma_obj(gem_obj);
|
||||
dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr);
|
||||
cma_obj->vaddr = NULL;
|
||||
}
|
||||
|
||||
drm_gem_cma_free_object(gem_obj);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object);
|
||||
|
||||
const struct file_operations tinydrm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
EXPORT_SYMBOL(tinydrm_fops);
|
||||
|
||||
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_fb_cma_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,
|
||||
.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
|
||||
* drm_dev_unref() frees the structure. The devm_ functions provide
|
||||
* for easy error handling.
|
||||
*/
|
||||
drm = drm_dev_alloc(driver, parent);
|
||||
if (IS_ERR(drm))
|
||||
return PTR_ERR(drm);
|
||||
|
||||
tdev->drm = drm;
|
||||
drm->dev_private = tdev;
|
||||
drm_mode_config_init(drm);
|
||||
drm->mode_config.funcs = &tinydrm_mode_config_funcs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_unref(tdev->drm);
|
||||
}
|
||||
|
||||
static void devm_tinydrm_release(void *data)
|
||||
{
|
||||
tinydrm_fini(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
|
||||
* mode_config. Resources will be automatically freed on driver detach (devres)
|
||||
* using drm_mode_config_cleanup() and drm_dev_unref().
|
||||
*
|
||||
* Returns:
|
||||
* 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);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action(parent, devm_tinydrm_release, tdev);
|
||||
if (ret)
|
||||
tinydrm_fini(tdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_tinydrm_init);
|
||||
|
||||
static int tinydrm_register(struct tinydrm_device *tdev)
|
||||
{
|
||||
struct drm_device *drm = tdev->drm;
|
||||
int bpp = drm->mode_config.preferred_depth;
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
int ret;
|
||||
|
||||
ret = drm_dev_register(tdev->drm, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fbdev = drm_fbdev_cma_init_with_funcs(drm, bpp ? bpp : 32,
|
||||
drm->mode_config.num_connector,
|
||||
tdev->fb_funcs);
|
||||
if (IS_ERR(fbdev))
|
||||
DRM_ERROR("Failed to initialize fbdev: %ld\n", PTR_ERR(fbdev));
|
||||
else
|
||||
tdev->fbdev_cma = fbdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tinydrm_unregister(struct tinydrm_device *tdev)
|
||||
{
|
||||
struct drm_fbdev_cma *fbdev_cma = tdev->fbdev_cma;
|
||||
|
||||
drm_crtc_force_disable_all(tdev->drm);
|
||||
/* don't restore fbdev in lastclose, keep pipeline disabled */
|
||||
tdev->fbdev_cma = NULL;
|
||||
drm_dev_unregister(tdev->drm);
|
||||
if (fbdev_cma)
|
||||
drm_fbdev_cma_fini(fbdev_cma);
|
||||
}
|
||||
|
||||
static void devm_tinydrm_register_release(void *data)
|
||||
{
|
||||
tinydrm_unregister(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_tinydrm_register - Register tinydrm device
|
||||
* @tdev: tinydrm device
|
||||
*
|
||||
* This function registers the underlying DRM device and fbdev.
|
||||
* These resources will be automatically unregistered on driver detach (devres)
|
||||
* and the display pipeline will be disabled.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int devm_tinydrm_register(struct tinydrm_device *tdev)
|
||||
{
|
||||
struct device *dev = tdev->drm->dev;
|
||||
int ret;
|
||||
|
||||
ret = tinydrm_register(tdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
|
||||
if (ret)
|
||||
tinydrm_unregister(tdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_tinydrm_register);
|
||||
|
||||
/**
|
||||
* tinydrm_shutdown - Shutdown tinydrm
|
||||
* @tdev: tinydrm device
|
||||
*
|
||||
* This function makes sure that the display pipeline is disabled.
|
||||
* Used by drivers in their shutdown callback to turn off the display
|
||||
* on machine shutdown and reboot.
|
||||
*/
|
||||
void tinydrm_shutdown(struct tinydrm_device *tdev)
|
||||
{
|
||||
drm_crtc_force_disable_all(tdev->drm);
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_shutdown);
|
||||
|
||||
/**
|
||||
* tinydrm_suspend - Suspend tinydrm
|
||||
* @tdev: tinydrm device
|
||||
*
|
||||
* Used in driver PM operations to suspend tinydrm.
|
||||
* Suspends fbdev and DRM.
|
||||
* Resume with tinydrm_resume().
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int tinydrm_suspend(struct tinydrm_device *tdev)
|
||||
{
|
||||
struct drm_atomic_state *state;
|
||||
|
||||
if (tdev->suspend_state) {
|
||||
DRM_ERROR("Failed to suspend: state already set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1);
|
||||
state = drm_atomic_helper_suspend(tdev->drm);
|
||||
if (IS_ERR(state)) {
|
||||
drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
|
||||
return PTR_ERR(state);
|
||||
}
|
||||
|
||||
tdev->suspend_state = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_suspend);
|
||||
|
||||
/**
|
||||
* tinydrm_resume - Resume tinydrm
|
||||
* @tdev: tinydrm device
|
||||
*
|
||||
* Used in driver PM operations to resume tinydrm.
|
||||
* Suspend with tinydrm_suspend().
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int tinydrm_resume(struct tinydrm_device *tdev)
|
||||
{
|
||||
struct drm_atomic_state *state = tdev->suspend_state;
|
||||
int ret;
|
||||
|
||||
if (!state) {
|
||||
DRM_ERROR("Failed to resume: state is not set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tdev->suspend_state = NULL;
|
||||
|
||||
ret = drm_atomic_helper_resume(tdev->drm, state);
|
||||
if (ret) {
|
||||
DRM_ERROR("Error resuming state: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_resume);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
460
drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
Normal file
460
drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
Normal file
@ -0,0 +1,460 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Noralf Trønnes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/tinydrm/tinydrm.h>
|
||||
#include <drm/tinydrm/tinydrm-helpers.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/swab.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);
|
||||
|
||||
/**
|
||||
* tinydrm_memcpy - Copy clip buffer
|
||||
* @dst: Destination buffer
|
||||
* @vaddr: Source buffer
|
||||
* @fb: DRM framebuffer
|
||||
* @clip: Clip rectangle area to copy
|
||||
*/
|
||||
void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
|
||||
struct drm_clip_rect *clip)
|
||||
{
|
||||
unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
|
||||
unsigned int pitch = fb->pitches[0];
|
||||
void *src = vaddr + (clip->y1 * pitch) + (clip->x1 * cpp);
|
||||
size_t len = (clip->x2 - clip->x1) * cpp;
|
||||
unsigned int y;
|
||||
|
||||
for (y = clip->y1; y < clip->y2; y++) {
|
||||
memcpy(dst, src, len);
|
||||
src += pitch;
|
||||
dst += len;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_memcpy);
|
||||
|
||||
/**
|
||||
* tinydrm_swab16 - Swap bytes into clip buffer
|
||||
* @dst: RGB565 destination buffer
|
||||
* @vaddr: RGB565 source buffer
|
||||
* @fb: DRM framebuffer
|
||||
* @clip: Clip rectangle area to copy
|
||||
*/
|
||||
void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
|
||||
struct drm_clip_rect *clip)
|
||||
{
|
||||
size_t len = (clip->x2 - clip->x1) * sizeof(u16);
|
||||
unsigned int x, y;
|
||||
u16 *src, *buf;
|
||||
|
||||
/*
|
||||
* The cma memory is write-combined so reads are uncached.
|
||||
* Speed up by fetching one line at a time.
|
||||
*/
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
for (y = clip->y1; y < clip->y2; y++) {
|
||||
src = vaddr + (y * fb->pitches[0]);
|
||||
src += clip->x1;
|
||||
memcpy(buf, src, len);
|
||||
src = buf;
|
||||
for (x = clip->x1; x < clip->x2; x++)
|
||||
*dst++ = swab16(*src++);
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_swab16);
|
||||
|
||||
/**
|
||||
* tinydrm_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
|
||||
* @dst: RGB565 destination buffer
|
||||
* @vaddr: XRGB8888 source buffer
|
||||
* @fb: DRM framebuffer
|
||||
* @clip: Clip rectangle area to copy
|
||||
* @swap: Swap bytes
|
||||
*
|
||||
* Drivers can use this function for RGB565 devices that don't natively
|
||||
* support XRGB8888.
|
||||
*/
|
||||
void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr,
|
||||
struct drm_framebuffer *fb,
|
||||
struct drm_clip_rect *clip, bool swap)
|
||||
{
|
||||
size_t len = (clip->x2 - clip->x1) * sizeof(u32);
|
||||
unsigned int x, y;
|
||||
u32 *src, *buf;
|
||||
u16 val16;
|
||||
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
for (y = clip->y1; y < clip->y2; y++) {
|
||||
src = vaddr + (y * fb->pitches[0]);
|
||||
src += clip->x1;
|
||||
memcpy(buf, src, len);
|
||||
src = buf;
|
||||
for (x = clip->x1; x < clip->x2; x++) {
|
||||
val16 = ((*src & 0x00F80000) >> 8) |
|
||||
((*src & 0x0000FC00) >> 5) |
|
||||
((*src & 0x000000F8) >> 3);
|
||||
src++;
|
||||
if (swap)
|
||||
*dst++ = swab16(val16);
|
||||
else
|
||||
*dst++ = val16;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_xrgb8888_to_rgb565);
|
||||
|
||||
/**
|
||||
* tinydrm_of_find_backlight - Find backlight device in device-tree
|
||||
* @dev: Device
|
||||
*
|
||||
* This function looks for a DT node pointed to by a property named 'backlight'
|
||||
* and uses of_find_backlight_by_node() to get the backlight device.
|
||||
* Additionally if the brightness property is zero, it is set to
|
||||
* max_brightness.
|
||||
*
|
||||
* Returns:
|
||||
* NULL if there's no backlight property.
|
||||
* Error pointer -EPROBE_DEFER if the DT node is found, but no backlight device
|
||||
* is found.
|
||||
* If the backlight device is found, a pointer to the structure is returned.
|
||||
*/
|
||||
struct backlight_device *tinydrm_of_find_backlight(struct device *dev)
|
||||
{
|
||||
struct backlight_device *backlight;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(dev->of_node, "backlight", 0);
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
backlight = of_find_backlight_by_node(np);
|
||||
of_node_put(np);
|
||||
|
||||
if (!backlight)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
if (!backlight->props.brightness) {
|
||||
backlight->props.brightness = backlight->props.max_brightness;
|
||||
DRM_DEBUG_KMS("Backlight brightness set to %d\n",
|
||||
backlight->props.brightness);
|
||||
}
|
||||
|
||||
return backlight;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_of_find_backlight);
|
||||
|
||||
/**
|
||||
* tinydrm_enable_backlight - Enable backlight helper
|
||||
* @backlight: Backlight device
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int tinydrm_enable_backlight(struct backlight_device *backlight)
|
||||
{
|
||||
unsigned int old_state;
|
||||
int ret;
|
||||
|
||||
if (!backlight)
|
||||
return 0;
|
||||
|
||||
old_state = backlight->props.state;
|
||||
backlight->props.state &= ~BL_CORE_FBBLANK;
|
||||
DRM_DEBUG_KMS("Backlight state: 0x%x -> 0x%x\n", old_state,
|
||||
backlight->props.state);
|
||||
|
||||
ret = backlight_update_status(backlight);
|
||||
if (ret)
|
||||
DRM_ERROR("Failed to enable backlight %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_enable_backlight);
|
||||
|
||||
/**
|
||||
* tinydrm_disable_backlight - Disable backlight helper
|
||||
* @backlight: Backlight device
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int tinydrm_disable_backlight(struct backlight_device *backlight)
|
||||
{
|
||||
unsigned int old_state;
|
||||
int ret;
|
||||
|
||||
if (!backlight)
|
||||
return 0;
|
||||
|
||||
old_state = backlight->props.state;
|
||||
backlight->props.state |= BL_CORE_FBBLANK;
|
||||
DRM_DEBUG_KMS("Backlight state: 0x%x -> 0x%x\n", old_state,
|
||||
backlight->props.state);
|
||||
ret = backlight_update_status(backlight);
|
||||
if (ret)
|
||||
DRM_ERROR("Failed to disable backlight %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_disable_backlight);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI)
|
||||
|
||||
/**
|
||||
* tinydrm_spi_max_transfer_size - Determine max SPI transfer size
|
||||
* @spi: SPI device
|
||||
* @max_len: Maximum buffer size needed (optional)
|
||||
*
|
||||
* This function returns the maximum size to use for SPI transfers. It checks
|
||||
* the SPI master, the optional @max_len and the module parameter spi_max and
|
||||
* returns the smallest.
|
||||
*
|
||||
* Returns:
|
||||
* Maximum size for SPI transfers
|
||||
*/
|
||||
size_t tinydrm_spi_max_transfer_size(struct spi_device *spi, size_t max_len)
|
||||
{
|
||||
size_t ret;
|
||||
|
||||
ret = min(spi_max_transfer_size(spi), spi->master->max_dma_len);
|
||||
if (max_len)
|
||||
ret = min(ret, max_len);
|
||||
if (spi_max)
|
||||
ret = min_t(size_t, ret, spi_max);
|
||||
ret &= ~0x3;
|
||||
if (ret < 4)
|
||||
ret = 4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_spi_max_transfer_size);
|
||||
|
||||
/**
|
||||
* tinydrm_spi_bpw_supported - Check if bits per word is supported
|
||||
* @spi: SPI device
|
||||
* @bpw: Bits per word
|
||||
*
|
||||
* This function checks to see if the SPI master driver supports @bpw.
|
||||
*
|
||||
* Returns:
|
||||
* True if @bpw is supported, false otherwise.
|
||||
*/
|
||||
bool tinydrm_spi_bpw_supported(struct spi_device *spi, u8 bpw)
|
||||
{
|
||||
u32 bpw_mask = spi->master->bits_per_word_mask;
|
||||
|
||||
if (bpw == 8)
|
||||
return true;
|
||||
|
||||
if (!bpw_mask) {
|
||||
dev_warn_once(&spi->dev,
|
||||
"bits_per_word_mask not set, assume 8-bit only\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bpw_mask & SPI_BPW_MASK(bpw))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_spi_bpw_supported);
|
||||
|
||||
static void
|
||||
tinydrm_dbg_spi_print(struct spi_device *spi, struct spi_transfer *tr,
|
||||
const void *buf, int idx, bool tx)
|
||||
{
|
||||
u32 speed_hz = tr->speed_hz ? tr->speed_hz : spi->max_speed_hz;
|
||||
char linebuf[3 * 32];
|
||||
|
||||
hex_dump_to_buffer(buf, tr->len, 16,
|
||||
DIV_ROUND_UP(tr->bits_per_word, 8),
|
||||
linebuf, sizeof(linebuf), false);
|
||||
|
||||
printk(KERN_DEBUG
|
||||
" tr(%i): speed=%u%s, bpw=%i, len=%u, %s_buf=[%s%s]\n", idx,
|
||||
speed_hz > 1000000 ? speed_hz / 1000000 : speed_hz / 1000,
|
||||
speed_hz > 1000000 ? "MHz" : "kHz", tr->bits_per_word, tr->len,
|
||||
tx ? "tx" : "rx", linebuf, tr->len > 16 ? " ..." : "");
|
||||
}
|
||||
|
||||
/* called through tinydrm_dbg_spi_message() */
|
||||
void _tinydrm_dbg_spi_message(struct spi_device *spi, struct spi_message *m)
|
||||
{
|
||||
struct spi_transfer *tmp;
|
||||
struct list_head *pos;
|
||||
int i = 0;
|
||||
|
||||
list_for_each(pos, &m->transfers) {
|
||||
tmp = list_entry(pos, struct spi_transfer, transfer_list);
|
||||
|
||||
if (tmp->tx_buf)
|
||||
tinydrm_dbg_spi_print(spi, tmp, tmp->tx_buf, i, true);
|
||||
if (tmp->rx_buf)
|
||||
tinydrm_dbg_spi_print(spi, tmp, tmp->rx_buf, i, false);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(_tinydrm_dbg_spi_message);
|
||||
|
||||
/**
|
||||
* tinydrm_spi_transfer - SPI transfer helper
|
||||
* @spi: SPI device
|
||||
* @speed_hz: Override speed (optional)
|
||||
* @header: Optional header transfer
|
||||
* @bpw: Bits per word
|
||||
* @buf: Buffer to transfer
|
||||
* @len: Buffer length
|
||||
*
|
||||
* This SPI transfer helper breaks up the transfer of @buf into chunks which
|
||||
* the SPI master driver can handle. If the machine is Little Endian and the
|
||||
* SPI master driver doesn't support 16 bits per word, it swaps the bytes and
|
||||
* does a 8-bit transfer.
|
||||
* If @header is set, it is prepended to each SPI message.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz,
|
||||
struct spi_transfer *header, u8 bpw, const void *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct spi_transfer tr = {
|
||||
.bits_per_word = bpw,
|
||||
.speed_hz = speed_hz,
|
||||
};
|
||||
struct spi_message m;
|
||||
u16 *swap_buf = NULL;
|
||||
size_t max_chunk;
|
||||
size_t chunk;
|
||||
int ret = 0;
|
||||
|
||||
if (WARN_ON_ONCE(bpw != 8 && bpw != 16))
|
||||
return -EINVAL;
|
||||
|
||||
max_chunk = tinydrm_spi_max_transfer_size(spi, 0);
|
||||
|
||||
if (drm_debug & DRM_UT_DRIVER)
|
||||
pr_debug("[drm:%s] bpw=%u, max_chunk=%zu, transfers:\n",
|
||||
__func__, bpw, max_chunk);
|
||||
|
||||
if (bpw == 16 && !tinydrm_spi_bpw_supported(spi, 16)) {
|
||||
tr.bits_per_word = 8;
|
||||
if (tinydrm_machine_little_endian()) {
|
||||
swap_buf = kmalloc(min(len, max_chunk), GFP_KERNEL);
|
||||
if (!swap_buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
spi_message_init(&m);
|
||||
if (header)
|
||||
spi_message_add_tail(header, &m);
|
||||
spi_message_add_tail(&tr, &m);
|
||||
|
||||
while (len) {
|
||||
chunk = min(len, max_chunk);
|
||||
|
||||
tr.tx_buf = buf;
|
||||
tr.len = chunk;
|
||||
|
||||
if (swap_buf) {
|
||||
const u16 *buf16 = buf;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < chunk / 2; i++)
|
||||
swap_buf[i] = swab16(buf16[i]);
|
||||
|
||||
tr.tx_buf = swap_buf;
|
||||
}
|
||||
|
||||
buf += chunk;
|
||||
len -= chunk;
|
||||
|
||||
tinydrm_dbg_spi_message(spi, &m);
|
||||
ret = spi_sync(spi, &m);
|
||||
if (ret)
|
||||
return ret;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_spi_transfer);
|
||||
|
||||
#endif /* CONFIG_SPI */
|
234
drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
Normal file
234
drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
Normal file
@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Noralf Trønnes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/tinydrm/tinydrm.h>
|
||||
|
||||
struct tinydrm_connector {
|
||||
struct drm_connector base;
|
||||
const struct drm_display_mode *mode;
|
||||
};
|
||||
|
||||
static inline struct tinydrm_connector *
|
||||
to_tinydrm_connector(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct tinydrm_connector, base);
|
||||
}
|
||||
|
||||
static int tinydrm_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct tinydrm_connector *tconn = to_tinydrm_connector(connector);
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(connector->dev, tconn->mode);
|
||||
if (!mode) {
|
||||
DRM_ERROR("Failed to duplicate mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mode->name[0] == '\0')
|
||||
drm_mode_set_name(mode);
|
||||
|
||||
mode->type |= DRM_MODE_TYPE_PREFERRED;
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
if (mode->width_mm) {
|
||||
connector->display_info.width_mm = mode->width_mm;
|
||||
connector->display_info.height_mm = mode->height_mm;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs tinydrm_connector_hfuncs = {
|
||||
.get_modes = tinydrm_connector_get_modes,
|
||||
.best_encoder = drm_atomic_helper_best_encoder,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
tinydrm_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
if (drm_device_is_unplugged(connector->dev))
|
||||
return connector_status_disconnected;
|
||||
|
||||
return connector->status;
|
||||
}
|
||||
|
||||
static void tinydrm_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct tinydrm_connector *tconn = to_tinydrm_connector(connector);
|
||||
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(tconn);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs tinydrm_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.detect = tinydrm_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = tinydrm_connector_destroy,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
struct drm_connector *
|
||||
tinydrm_connector_create(struct drm_device *drm,
|
||||
const struct drm_display_mode *mode,
|
||||
int connector_type)
|
||||
{
|
||||
struct tinydrm_connector *tconn;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
tconn = kzalloc(sizeof(*tconn), GFP_KERNEL);
|
||||
if (!tconn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
tconn->mode = mode;
|
||||
connector = &tconn->base;
|
||||
|
||||
drm_connector_helper_add(connector, &tinydrm_connector_hfuncs);
|
||||
ret = drm_connector_init(drm, connector, &tinydrm_connector_funcs,
|
||||
connector_type);
|
||||
if (ret) {
|
||||
kfree(tconn);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
connector->status = connector_status_connected;
|
||||
|
||||
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)) {
|
||||
pipe->plane.fb = fb;
|
||||
if (fb->funcs->dirty)
|
||||
fb->funcs->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);
|
||||
|
||||
/**
|
||||
* tinydrm_display_pipe_prepare_fb - Display pipe prepare_fb helper
|
||||
* @pipe: Simple display pipe
|
||||
* @plane_state: Plane state
|
||||
*
|
||||
* This function uses drm_fb_cma_prepare_fb() to check if the plane FB has an
|
||||
* dma-buf attached, extracts the exclusive fence and attaches it to plane
|
||||
* state for the atomic helper to wait on. Drivers can use this as their
|
||||
* &drm_simple_display_pipe_funcs->prepare_fb callback.
|
||||
*/
|
||||
int tinydrm_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_display_pipe_prepare_fb);
|
||||
|
||||
static int tinydrm_rotate_mode(struct drm_display_mode *mode,
|
||||
unsigned int rotation)
|
||||
{
|
||||
if (rotation == 0 || rotation == 180) {
|
||||
return 0;
|
||||
} else if (rotation == 90 || rotation == 270) {
|
||||
swap(mode->hdisplay, mode->vdisplay);
|
||||
swap(mode->hsync_start, mode->vsync_start);
|
||||
swap(mode->hsync_end, mode->vsync_end);
|
||||
swap(mode->htotal, mode->vtotal);
|
||||
swap(mode->width_mm, mode->height_mm);
|
||||
return 0;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tinydrm_display_pipe_init - Initialize display pipe
|
||||
* @tdev: tinydrm device
|
||||
* @funcs: Display pipe functions
|
||||
* @connector_type: Connector type
|
||||
* @formats: Array of supported formats (DRM_FORMAT\_\*)
|
||||
* @format_count: Number of elements in @formats
|
||||
* @mode: Supported mode
|
||||
* @rotation: Initial @mode rotation in degrees Counter Clock Wise
|
||||
*
|
||||
* This function sets up a &drm_simple_display_pipe with a &drm_connector that
|
||||
* has one fixed &drm_display_mode which is rotated according to @rotation.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int
|
||||
tinydrm_display_pipe_init(struct tinydrm_device *tdev,
|
||||
const struct drm_simple_display_pipe_funcs *funcs,
|
||||
int connector_type,
|
||||
const uint32_t *formats,
|
||||
unsigned int format_count,
|
||||
const struct drm_display_mode *mode,
|
||||
unsigned int rotation)
|
||||
{
|
||||
struct drm_device *drm = tdev->drm;
|
||||
struct drm_display_mode *mode_copy;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
mode_copy = devm_kmalloc(drm->dev, sizeof(*mode_copy), GFP_KERNEL);
|
||||
if (!mode_copy)
|
||||
return -ENOMEM;
|
||||
|
||||
*mode_copy = *mode;
|
||||
ret = tinydrm_rotate_mode(mode_copy, rotation);
|
||||
if (ret) {
|
||||
DRM_ERROR("Illegal rotation value %u\n", rotation);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drm->mode_config.min_width = mode_copy->hdisplay;
|
||||
drm->mode_config.max_width = mode_copy->hdisplay;
|
||||
drm->mode_config.min_height = mode_copy->vdisplay;
|
||||
drm->mode_config.max_height = mode_copy->vdisplay;
|
||||
|
||||
connector = tinydrm_connector_create(drm, mode_copy, connector_type);
|
||||
if (IS_ERR(connector))
|
||||
return PTR_ERR(connector);
|
||||
|
||||
ret = drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats,
|
||||
format_count, connector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_display_pipe_init);
|
279
drivers/gpu/drm/tinydrm/mi0283qt.c
Normal file
279
drivers/gpu/drm/tinydrm/mi0283qt.c
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* DRM driver for Multi-Inno MI0283QT panels
|
||||
*
|
||||
* Copyright 2016 Noralf Trønnes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/tinydrm/ili9341.h>
|
||||
#include <drm/tinydrm/mipi-dbi.h>
|
||||
#include <drm/tinydrm/tinydrm-helpers.h>
|
||||
#include <linux/delay.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 <video/mipi_display.h>
|
||||
|
||||
static int mi0283qt_init(struct mipi_dbi *mipi)
|
||||
{
|
||||
struct tinydrm_device *tdev = &mipi->tinydrm;
|
||||
struct device *dev = tdev->drm->dev;
|
||||
u8 addr_mode;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
ret = regulator_enable(mipi->regulator);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable regulator %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Avoid flicker by skipping setup if the bootloader has done it */
|
||||
if (mipi_dbi_display_is_on(mipi))
|
||||
return 0;
|
||||
|
||||
mipi_dbi_hw_reset(mipi);
|
||||
ret = mipi_dbi_command(mipi, MIPI_DCS_SOFT_RESET);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error sending command %d\n", ret);
|
||||
regulator_disable(mipi->regulator);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_OFF);
|
||||
|
||||
mipi_dbi_command(mipi, ILI9341_PWCTRLB, 0x00, 0x83, 0x30);
|
||||
mipi_dbi_command(mipi, ILI9341_PWRSEQ, 0x64, 0x03, 0x12, 0x81);
|
||||
mipi_dbi_command(mipi, ILI9341_DTCTRLA, 0x85, 0x01, 0x79);
|
||||
mipi_dbi_command(mipi, ILI9341_PWCTRLA, 0x39, 0x2c, 0x00, 0x34, 0x02);
|
||||
mipi_dbi_command(mipi, ILI9341_PUMPCTRL, 0x20);
|
||||
mipi_dbi_command(mipi, ILI9341_DTCTRLB, 0x00, 0x00);
|
||||
|
||||
/* Power Control */
|
||||
mipi_dbi_command(mipi, ILI9341_PWCTRL1, 0x26);
|
||||
mipi_dbi_command(mipi, ILI9341_PWCTRL2, 0x11);
|
||||
/* VCOM */
|
||||
mipi_dbi_command(mipi, ILI9341_VMCTRL1, 0x35, 0x3e);
|
||||
mipi_dbi_command(mipi, ILI9341_VMCTRL2, 0xbe);
|
||||
|
||||
/* Memory Access Control */
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_PIXEL_FORMAT, 0x55);
|
||||
|
||||
switch (mipi->rotation) {
|
||||
default:
|
||||
addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY |
|
||||
ILI9341_MADCTL_MX;
|
||||
break;
|
||||
case 90:
|
||||
addr_mode = ILI9341_MADCTL_MY;
|
||||
break;
|
||||
case 180:
|
||||
addr_mode = ILI9341_MADCTL_MV;
|
||||
break;
|
||||
case 270:
|
||||
addr_mode = ILI9341_MADCTL_MX;
|
||||
break;
|
||||
}
|
||||
addr_mode |= ILI9341_MADCTL_BGR;
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
|
||||
|
||||
/* Frame Rate */
|
||||
mipi_dbi_command(mipi, ILI9341_FRMCTR1, 0x00, 0x1b);
|
||||
|
||||
/* Gamma */
|
||||
mipi_dbi_command(mipi, ILI9341_EN3GAM, 0x08);
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
|
||||
mipi_dbi_command(mipi, ILI9341_PGAMCTRL,
|
||||
0x1f, 0x1a, 0x18, 0x0a, 0x0f, 0x06, 0x45, 0x87,
|
||||
0x32, 0x0a, 0x07, 0x02, 0x07, 0x05, 0x00);
|
||||
mipi_dbi_command(mipi, ILI9341_NGAMCTRL,
|
||||
0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3a, 0x78,
|
||||
0x4d, 0x05, 0x18, 0x0d, 0x38, 0x3a, 0x1f);
|
||||
|
||||
/* DDRAM */
|
||||
mipi_dbi_command(mipi, ILI9341_ETMOD, 0x07);
|
||||
|
||||
/* Display */
|
||||
mipi_dbi_command(mipi, ILI9341_DISCTRL, 0x0a, 0x82, 0x27, 0x00);
|
||||
mipi_dbi_command(mipi, MIPI_DCS_EXIT_SLEEP_MODE);
|
||||
msleep(100);
|
||||
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
|
||||
msleep(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mi0283qt_fini(void *data)
|
||||
{
|
||||
struct mipi_dbi *mipi = data;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
regulator_disable(mipi->regulator);
|
||||
}
|
||||
|
||||
static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = {
|
||||
.enable = mipi_dbi_pipe_enable,
|
||||
.disable = mipi_dbi_pipe_disable,
|
||||
.update = tinydrm_display_pipe_update,
|
||||
.prepare_fb = tinydrm_display_pipe_prepare_fb,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode mi0283qt_mode = {
|
||||
TINYDRM_MODE(320, 240, 58, 43),
|
||||
};
|
||||
|
||||
static struct drm_driver mi0283qt_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
|
||||
DRIVER_ATOMIC,
|
||||
TINYDRM_GEM_DRIVER_OPS,
|
||||
.lastclose = tinydrm_lastclose,
|
||||
.debugfs_init = mipi_dbi_debugfs_init,
|
||||
.name = "mi0283qt",
|
||||
.desc = "Multi-Inno MI0283QT",
|
||||
.date = "20160614",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
};
|
||||
|
||||
static const struct of_device_id mi0283qt_of_match[] = {
|
||||
{ .compatible = "multi-inno,mi0283qt" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mi0283qt_of_match);
|
||||
|
||||
static const struct spi_device_id mi0283qt_id[] = {
|
||||
{ "mi0283qt", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mi0283qt_id);
|
||||
|
||||
static int mi0283qt_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct tinydrm_device *tdev;
|
||||
struct mipi_dbi *mipi;
|
||||
struct gpio_desc *dc;
|
||||
u32 rotation = 0;
|
||||
int ret;
|
||||
|
||||
mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
|
||||
if (!mipi)
|
||||
return -ENOMEM;
|
||||
|
||||
mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(mipi->reset)) {
|
||||
dev_err(dev, "Failed to get gpio 'reset'\n");
|
||||
return PTR_ERR(mipi->reset);
|
||||
}
|
||||
|
||||
dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(dc)) {
|
||||
dev_err(dev, "Failed to get gpio 'dc'\n");
|
||||
return PTR_ERR(dc);
|
||||
}
|
||||
|
||||
mipi->regulator = devm_regulator_get(dev, "power");
|
||||
if (IS_ERR(mipi->regulator))
|
||||
return PTR_ERR(mipi->regulator);
|
||||
|
||||
mipi->backlight = tinydrm_of_find_backlight(dev);
|
||||
if (IS_ERR(mipi->backlight))
|
||||
return PTR_ERR(mipi->backlight);
|
||||
|
||||
device_property_read_u32(dev, "rotation", &rotation);
|
||||
|
||||
ret = mipi_dbi_spi_init(spi, mipi, dc, &mi0283qt_pipe_funcs,
|
||||
&mi0283qt_driver, &mi0283qt_mode, rotation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mi0283qt_init(mipi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* use devres to fini after drm unregister (drv->remove is before) */
|
||||
ret = devm_add_action(dev, mi0283qt_fini, mipi);
|
||||
if (ret) {
|
||||
mi0283qt_fini(mipi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tdev = &mipi->tinydrm;
|
||||
|
||||
ret = devm_tinydrm_register(tdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, mipi);
|
||||
|
||||
DRM_DEBUG_DRIVER("Initialized %s:%s @%uMHz on minor %d\n",
|
||||
tdev->drm->driver->name, dev_name(dev),
|
||||
spi->max_speed_hz / 1000000,
|
||||
tdev->drm->primary->index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mi0283qt_shutdown(struct spi_device *spi)
|
||||
{
|
||||
struct mipi_dbi *mipi = spi_get_drvdata(spi);
|
||||
|
||||
tinydrm_shutdown(&mipi->tinydrm);
|
||||
}
|
||||
|
||||
static int __maybe_unused mi0283qt_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct mipi_dbi *mipi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = tinydrm_suspend(&mipi->tinydrm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mi0283qt_fini(mipi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mi0283qt_pm_resume(struct device *dev)
|
||||
{
|
||||
struct mipi_dbi *mipi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = mi0283qt_init(mipi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return tinydrm_resume(&mipi->tinydrm);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mi0283qt_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mi0283qt_pm_suspend, mi0283qt_pm_resume)
|
||||
};
|
||||
|
||||
static struct spi_driver mi0283qt_spi_driver = {
|
||||
.driver = {
|
||||
.name = "mi0283qt",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mi0283qt_of_match,
|
||||
.pm = &mi0283qt_pm_ops,
|
||||
},
|
||||
.id_table = mi0283qt_id,
|
||||
.probe = mi0283qt_probe,
|
||||
.shutdown = mi0283qt_shutdown,
|
||||
};
|
||||
module_spi_driver(mi0283qt_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Multi-Inno MI0283QT DRM driver");
|
||||
MODULE_AUTHOR("Noralf Trønnes");
|
||||
MODULE_LICENSE("GPL");
|
1005
drivers/gpu/drm/tinydrm/mipi-dbi.c
Normal file
1005
drivers/gpu/drm/tinydrm/mipi-dbi.c
Normal file
File diff suppressed because it is too large
Load Diff
54
include/drm/tinydrm/ili9341.h
Normal file
54
include/drm/tinydrm/ili9341.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* ILI9341 LCD controller
|
||||
*
|
||||
* Copyright 2016 Noralf Trønnes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_ILI9341_H
|
||||
#define __LINUX_ILI9341_H
|
||||
|
||||
#define ILI9341_FRMCTR1 0xb1
|
||||
#define ILI9341_FRMCTR2 0xb2
|
||||
#define ILI9341_FRMCTR3 0xb3
|
||||
#define ILI9341_INVTR 0xb4
|
||||
#define ILI9341_PRCTR 0xb5
|
||||
#define ILI9341_DISCTRL 0xb6
|
||||
#define ILI9341_ETMOD 0xb7
|
||||
|
||||
#define ILI9341_PWCTRL1 0xc0
|
||||
#define ILI9341_PWCTRL2 0xc1
|
||||
#define ILI9341_VMCTRL1 0xc5
|
||||
#define ILI9341_VMCTRL2 0xc7
|
||||
#define ILI9341_PWCTRLA 0xcb
|
||||
#define ILI9341_PWCTRLB 0xcf
|
||||
|
||||
#define ILI9341_RDID1 0xda
|
||||
#define ILI9341_RDID2 0xdb
|
||||
#define ILI9341_RDID3 0xdc
|
||||
#define ILI9341_RDID4 0xd3
|
||||
|
||||
#define ILI9341_PGAMCTRL 0xe0
|
||||
#define ILI9341_NGAMCTRL 0xe1
|
||||
#define ILI9341_DGAMCTRL1 0xe2
|
||||
#define ILI9341_DGAMCTRL2 0xe3
|
||||
#define ILI9341_DTCTRLA 0xe8
|
||||
#define ILI9341_DTCTRLB 0xea
|
||||
#define ILI9341_PWRSEQ 0xed
|
||||
|
||||
#define ILI9341_EN3GAM 0xf2
|
||||
#define ILI9341_IFCTRL 0xf6
|
||||
#define ILI9341_PUMPCTRL 0xf7
|
||||
|
||||
#define ILI9341_MADCTL_MH BIT(2)
|
||||
#define ILI9341_MADCTL_BGR BIT(3)
|
||||
#define ILI9341_MADCTL_ML BIT(4)
|
||||
#define ILI9341_MADCTL_MV BIT(5)
|
||||
#define ILI9341_MADCTL_MX BIT(6)
|
||||
#define ILI9341_MADCTL_MY BIT(7)
|
||||
|
||||
#endif /* __LINUX_ILI9341_H */
|
107
include/drm/tinydrm/mipi-dbi.h
Normal file
107
include/drm/tinydrm/mipi-dbi.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* MIPI Display Bus Interface (DBI) LCD controller support
|
||||
*
|
||||
* Copyright 2016 Noralf Trønnes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MIPI_DBI_H
|
||||
#define __LINUX_MIPI_DBI_H
|
||||
|
||||
#include <drm/tinydrm/tinydrm.h>
|
||||
|
||||
struct spi_device;
|
||||
struct gpio_desc;
|
||||
struct regulator;
|
||||
|
||||
/**
|
||||
* struct mipi_dbi - MIPI DBI controller
|
||||
* @tinydrm: tinydrm base
|
||||
* @spi: SPI device
|
||||
* @enabled: Pipeline is enabled
|
||||
* @cmdlock: Command lock
|
||||
* @command: Bus specific callback executing commands.
|
||||
* @read_commands: Array of read commands terminated by a zero entry.
|
||||
* Reading is disabled if this is NULL.
|
||||
* @dc: Optional D/C gpio.
|
||||
* @tx_buf: Buffer used for transfer (copy clip rect area)
|
||||
* @tx_buf9: Buffer used for Option 1 9-bit conversion
|
||||
* @tx_buf9_len: Size of tx_buf9.
|
||||
* @swap_bytes: Swap bytes in buffer before transfer
|
||||
* @reset: Optional reset gpio
|
||||
* @rotation: initial rotation in degrees Counter Clock Wise
|
||||
* @backlight: backlight device (optional)
|
||||
* @regulator: power regulator (optional)
|
||||
*/
|
||||
struct mipi_dbi {
|
||||
struct tinydrm_device tinydrm;
|
||||
struct spi_device *spi;
|
||||
bool enabled;
|
||||
struct mutex cmdlock;
|
||||
int (*command)(struct mipi_dbi *mipi, u8 cmd, u8 *param, size_t num);
|
||||
const u8 *read_commands;
|
||||
struct gpio_desc *dc;
|
||||
u16 *tx_buf;
|
||||
void *tx_buf9;
|
||||
size_t tx_buf9_len;
|
||||
bool swap_bytes;
|
||||
struct gpio_desc *reset;
|
||||
unsigned int rotation;
|
||||
struct backlight_device *backlight;
|
||||
struct regulator *regulator;
|
||||
};
|
||||
|
||||
static inline struct mipi_dbi *
|
||||
mipi_dbi_from_tinydrm(struct tinydrm_device *tdev)
|
||||
{
|
||||
return container_of(tdev, struct mipi_dbi, tinydrm);
|
||||
}
|
||||
|
||||
int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi,
|
||||
struct gpio_desc *dc,
|
||||
const struct drm_simple_display_pipe_funcs *pipe_funcs,
|
||||
struct drm_driver *driver,
|
||||
const struct drm_display_mode *mode,
|
||||
unsigned int rotation);
|
||||
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_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state);
|
||||
void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe);
|
||||
void mipi_dbi_hw_reset(struct mipi_dbi *mipi);
|
||||
bool mipi_dbi_display_is_on(struct mipi_dbi *mipi);
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* mipi_dbi_command - MIPI DCS command with optional parameter(s)
|
||||
* @mipi: MIPI structure
|
||||
* @cmd: Command
|
||||
* @seq...: Optional parameter(s)
|
||||
*
|
||||
* Send MIPI DCS command to the controller. Use mipi_dbi_command_read() for
|
||||
* get/read.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
#define mipi_dbi_command(mipi, cmd, seq...) \
|
||||
({ \
|
||||
u8 d[] = { seq }; \
|
||||
mipi_dbi_command_buf(mipi, cmd, d, ARRAY_SIZE(d)); \
|
||||
})
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
int mipi_dbi_debugfs_init(struct drm_minor *minor);
|
||||
#else
|
||||
#define mipi_dbi_debugfs_init NULL
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_MIPI_DBI_H */
|
100
include/drm/tinydrm/tinydrm-helpers.h
Normal file
100
include/drm/tinydrm/tinydrm-helpers.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Noralf Trønnes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_TINYDRM_HELPERS_H
|
||||
#define __LINUX_TINYDRM_HELPERS_H
|
||||
|
||||
struct backlight_device;
|
||||
struct tinydrm_device;
|
||||
struct drm_clip_rect;
|
||||
struct spi_transfer;
|
||||
struct spi_message;
|
||||
struct spi_device;
|
||||
struct device;
|
||||
|
||||
/**
|
||||
* tinydrm_machine_little_endian - Machine is little endian
|
||||
*
|
||||
* Returns:
|
||||
* true if *defined(__LITTLE_ENDIAN)*, false otherwise
|
||||
*/
|
||||
static inline bool tinydrm_machine_little_endian(void)
|
||||
{
|
||||
#if defined(__LITTLE_ENDIAN)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#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);
|
||||
void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
|
||||
struct drm_clip_rect *clip);
|
||||
void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
|
||||
struct drm_clip_rect *clip);
|
||||
void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr,
|
||||
struct drm_framebuffer *fb,
|
||||
struct drm_clip_rect *clip, bool swap);
|
||||
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct backlight_device *tinydrm_of_find_backlight(struct device *dev);
|
||||
int tinydrm_enable_backlight(struct backlight_device *backlight);
|
||||
int tinydrm_disable_backlight(struct backlight_device *backlight);
|
||||
#else
|
||||
static inline struct backlight_device *
|
||||
tinydrm_of_find_backlight(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int tinydrm_enable_backlight(struct backlight_device *backlight)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
tinydrm_disable_backlight(struct backlight_device *backlight)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz,
|
||||
struct spi_transfer *header, u8 bpw, const void *buf,
|
||||
size_t len);
|
||||
void _tinydrm_dbg_spi_message(struct spi_device *spi, struct spi_message *m);
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* tinydrm_dbg_spi_message - Dump SPI message
|
||||
* @spi: SPI device
|
||||
* @m: SPI message
|
||||
*
|
||||
* Dumps info about the transfers in a SPI message including buffer content.
|
||||
* DEBUG has to be defined for this function to be enabled alongside setting
|
||||
* the DRM_UT_DRIVER bit of &drm_debug.
|
||||
*/
|
||||
static inline void tinydrm_dbg_spi_message(struct spi_device *spi,
|
||||
struct spi_message *m)
|
||||
{
|
||||
if (drm_debug & DRM_UT_DRIVER)
|
||||
_tinydrm_dbg_spi_message(spi, m);
|
||||
}
|
||||
#else
|
||||
static inline void tinydrm_dbg_spi_message(struct spi_device *spi,
|
||||
struct spi_message *m)
|
||||
{
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* __LINUX_TINYDRM_HELPERS_H */
|
115
include/drm/tinydrm/tinydrm.h
Normal file
115
include/drm/tinydrm/tinydrm.h
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Noralf Trønnes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_TINYDRM_H
|
||||
#define __LINUX_TINYDRM_H
|
||||
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
|
||||
/**
|
||||
* struct tinydrm_device - tinydrm device
|
||||
* @drm: DRM device
|
||||
* @pipe: Display pipe structure
|
||||
* @dirty_lock: Serializes framebuffer flushing
|
||||
* @fbdev_cma: CMA fbdev structure
|
||||
* @suspend_state: Atomic state when suspended
|
||||
* @fb_funcs: Framebuffer functions used when creating framebuffers
|
||||
*/
|
||||
struct tinydrm_device {
|
||||
struct drm_device *drm;
|
||||
struct drm_simple_display_pipe pipe;
|
||||
struct mutex dirty_lock;
|
||||
struct drm_fbdev_cma *fbdev_cma;
|
||||
struct drm_atomic_state *suspend_state;
|
||||
const struct drm_framebuffer_funcs *fb_funcs;
|
||||
};
|
||||
|
||||
static inline struct tinydrm_device *
|
||||
pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
return container_of(pipe, struct tinydrm_device, pipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* TINYDRM_GEM_DRIVER_OPS - default tinydrm gem operations
|
||||
*
|
||||
* This macro provides a shortcut for setting the tinydrm GEM operations in
|
||||
* the &drm_driver structure.
|
||||
*/
|
||||
#define TINYDRM_GEM_DRIVER_OPS \
|
||||
.gem_free_object = tinydrm_gem_cma_free_object, \
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops, \
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd, \
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle, \
|
||||
.gem_prime_import = drm_gem_prime_import, \
|
||||
.gem_prime_export = drm_gem_prime_export, \
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, \
|
||||
.gem_prime_import_sg_table = tinydrm_gem_cma_prime_import_sg_table, \
|
||||
.gem_prime_vmap = drm_gem_cma_prime_vmap, \
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap, \
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap, \
|
||||
.dumb_create = drm_gem_cma_dumb_create, \
|
||||
.dumb_map_offset = drm_gem_cma_dumb_map_offset, \
|
||||
.dumb_destroy = drm_gem_dumb_destroy, \
|
||||
.fops = &tinydrm_fops
|
||||
|
||||
/**
|
||||
* TINYDRM_MODE - tinydrm display mode
|
||||
* @hd: Horizontal resolution, width
|
||||
* @vd: Vertical resolution, height
|
||||
* @hd_mm: Display width in millimeters
|
||||
* @vd_mm: Display height in millimeters
|
||||
*
|
||||
* This macro creates a &drm_display_mode for use with tinydrm.
|
||||
*/
|
||||
#define TINYDRM_MODE(hd, vd, hd_mm, vd_mm) \
|
||||
.hdisplay = (hd), \
|
||||
.hsync_start = (hd), \
|
||||
.hsync_end = (hd), \
|
||||
.htotal = (hd), \
|
||||
.vdisplay = (vd), \
|
||||
.vsync_start = (vd), \
|
||||
.vsync_end = (vd), \
|
||||
.vtotal = (vd), \
|
||||
.width_mm = (hd_mm), \
|
||||
.height_mm = (vd_mm), \
|
||||
.type = DRM_MODE_TYPE_DRIVER, \
|
||||
.clock = 1 /* pass validation */
|
||||
|
||||
extern const struct file_operations tinydrm_fops;
|
||||
void tinydrm_lastclose(struct drm_device *drm);
|
||||
void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj);
|
||||
struct drm_gem_object *
|
||||
tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm,
|
||||
struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt);
|
||||
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);
|
||||
int tinydrm_suspend(struct tinydrm_device *tdev);
|
||||
int tinydrm_resume(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_prepare_fb(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *plane_state);
|
||||
int
|
||||
tinydrm_display_pipe_init(struct tinydrm_device *tdev,
|
||||
const struct drm_simple_display_pipe_funcs *funcs,
|
||||
int connector_type,
|
||||
const uint32_t *formats,
|
||||
unsigned int format_count,
|
||||
const struct drm_display_mode *mode,
|
||||
unsigned int rotation);
|
||||
|
||||
#endif /* __LINUX_TINYDRM_H */
|
Loading…
Reference in New Issue
Block a user