drm-misc-next for v5.20:
UAPI Changes: Cross-subsystem Changes: * dma-buf: Add sync-file API; Set DMA mask for udmabuf devices * fbcon: Cleanups * fbdev: Disable firmware-device registration when first native driver loads * iosys-map: Documentation fixes Core Changes: * edid: Use struct drm_edid in more places * gem-cma-helper: Improve documentation * of: Add data-lane helpers and convert drivers * syncobj: Fixes Driver Changes: * amdgpu: Build fixes * ast: Support multiple outputs * bochs: Include <linux/module.h> * bridge: adv7511: I2C fixes; anx7625: Fix error handling; lt6505: Kconfig fixes * display/dp: Documentation fixes * display/dp-mst: Read extended DPCD capabilities during system resume * logicvc: Add new driver * magag200: Build fixes * nouveau: Cleanups * panel: Add backlight support; nt36672a: DT backlight support * qxl: Cleanups * sun4i: HDMI PHY cleanups * vc4: Add support for BCM2711 * virt-gpu: Avoid NULL dereference; Fix error checks; Cleanups * vkms: Allocate output buffer with vmalloc(); Fixes -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmKsKwAACgkQaA3BHVML eiPnJAgArdU7DOVUuRm0clRBHNndob37pICGbvdh84jNEpHq6rdfpi7HGqvNYvSy KyVhBPKFxygDyXNrgFh5YnMLX8Gr+S73VRKXc19k5tc3KYG9W36Vg/aBL7fXqs1F VDGgC0uksMIA2X0J03a+cZuQV8drYoWLUO7Pz0TcqFE+9cSSBe8gJb6lSt9rPIYx 0YTFWsHbL9VeW/VuhyG11KLL3s2SGofP4lw8i7H01RQJRGYo/Ak95ysA9Uo/4AXA nwOoK46MqPtsqs+dxGU/Ix6jP8KHSGMPKb+WEfv8WDQvdhOnOyUsza3V2Uma2H+M 2wlAkJnhyUXletzTRQBUnGFBHcccpA== =8nrd -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2022-06-17' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for v5.20: UAPI Changes: Cross-subsystem Changes: * dma-buf: Add sync-file API; Set DMA mask for udmabuf devices * fbcon: Cleanups * fbdev: Disable firmware-device registration when first native driver loads * iosys-map: Documentation fixes Core Changes: * edid: Use struct drm_edid in more places * gem-cma-helper: Improve documentation * of: Add data-lane helpers and convert drivers * syncobj: Fixes Driver Changes: * amdgpu: Build fixes * ast: Support multiple outputs * bochs: Include <linux/module.h> * bridge: adv7511: I2C fixes; anx7625: Fix error handling; lt6505: Kconfig fixes * display/dp: Documentation fixes * display/dp-mst: Read extended DPCD capabilities during system resume * logicvc: Add new driver * magag200: Build fixes * nouveau: Cleanups * panel: Add backlight support; nt36672a: DT backlight support * qxl: Cleanups * sun4i: HDMI PHY cleanups * vc4: Add support for BCM2711 * virt-gpu: Avoid NULL dereference; Fix error checks; Cleanups * vkms: Allocate output buffer with vmalloc(); Fixes Signed-off-by: Dave Airlie <airlied@redhat.com> From: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/YqwriEhn0l4uO+Gn@linux-uq9g
This commit is contained in:
commit
0a2af0bd6e
|
@ -46,6 +46,7 @@ properties:
|
|||
|
||||
reg: true
|
||||
port: true
|
||||
backlight: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
@ -73,6 +74,7 @@ examples:
|
|||
vddpos-supply = <&lab>;
|
||||
vddneg-supply = <&ibb>;
|
||||
|
||||
backlight = <&pmi8998_wled>;
|
||||
reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
port {
|
||||
|
|
|
@ -16,6 +16,7 @@ properties:
|
|||
|
||||
compatible:
|
||||
enum:
|
||||
- brcm,2711-v3d
|
||||
- brcm,7268-v3d
|
||||
- brcm,7278-v3d
|
||||
|
||||
|
|
|
@ -13,6 +13,12 @@ EDD Interfaces
|
|||
.. kernel-doc:: drivers/firmware/edd.c
|
||||
:internal:
|
||||
|
||||
Generic System Framebuffers Interface
|
||||
-------------------------------------
|
||||
|
||||
.. kernel-doc:: drivers/firmware/sysfb.c
|
||||
:export:
|
||||
|
||||
Intel Stratix10 SoC Service Layer
|
||||
---------------------------------
|
||||
Some features of the Intel Stratix10 SoC require a level of privilege
|
||||
|
|
|
@ -102,12 +102,6 @@ Debugging:
|
|||
|
||||
- kms_plane: some test cases are failing due to timeout on capturing CRC;
|
||||
|
||||
- kms_flip: when running test cases in sequence, some successful individual
|
||||
test cases are failing randomly; when individually, some successful test
|
||||
cases display in the log the following error::
|
||||
|
||||
[drm:vkms_prepare_fb [vkms]] ERROR vmap failed: -4
|
||||
|
||||
Virtual hardware (vblank-less) mode:
|
||||
|
||||
- VKMS already has support for vblanks simulated via hrtimers, which can be
|
||||
|
|
|
@ -6284,6 +6284,12 @@ S: Orphan / Obsolete
|
|||
F: drivers/gpu/drm/i810/
|
||||
F: include/uapi/drm/i810_drm.h
|
||||
|
||||
DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER
|
||||
M: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
S: Supported
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
F: drivers/gpu/drm/logicvc/
|
||||
|
||||
DRM DRIVER FOR LVDS PANELS
|
||||
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sync_file.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/dma-resv.h>
|
||||
#include <linux/mm.h>
|
||||
|
@ -192,6 +193,9 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)
|
|||
* Note that this only signals the completion of the respective fences, i.e. the
|
||||
* DMA transfers are complete. Cache flushing and any other necessary
|
||||
* preparations before CPU access can begin still need to happen.
|
||||
*
|
||||
* As an alternative to poll(), the set of fences on DMA buffer can be
|
||||
* exported as a &sync_file using &dma_buf_sync_file_export.
|
||||
*/
|
||||
|
||||
static void dma_buf_poll_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
|
||||
|
@ -326,6 +330,101 @@ static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SYNC_FILE)
|
||||
static long dma_buf_export_sync_file(struct dma_buf *dmabuf,
|
||||
void __user *user_data)
|
||||
{
|
||||
struct dma_buf_export_sync_file arg;
|
||||
enum dma_resv_usage usage;
|
||||
struct dma_fence *fence = NULL;
|
||||
struct sync_file *sync_file;
|
||||
int fd, ret;
|
||||
|
||||
if (copy_from_user(&arg, user_data, sizeof(arg)))
|
||||
return -EFAULT;
|
||||
|
||||
if (arg.flags & ~DMA_BUF_SYNC_RW)
|
||||
return -EINVAL;
|
||||
|
||||
if ((arg.flags & DMA_BUF_SYNC_RW) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
fd = get_unused_fd_flags(O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
usage = dma_resv_usage_rw(arg.flags & DMA_BUF_SYNC_WRITE);
|
||||
ret = dma_resv_get_singleton(dmabuf->resv, usage, &fence);
|
||||
if (ret)
|
||||
goto err_put_fd;
|
||||
|
||||
if (!fence)
|
||||
fence = dma_fence_get_stub();
|
||||
|
||||
sync_file = sync_file_create(fence);
|
||||
|
||||
dma_fence_put(fence);
|
||||
|
||||
if (!sync_file) {
|
||||
ret = -ENOMEM;
|
||||
goto err_put_fd;
|
||||
}
|
||||
|
||||
arg.fd = fd;
|
||||
if (copy_to_user(user_data, &arg, sizeof(arg))) {
|
||||
ret = -EFAULT;
|
||||
goto err_put_file;
|
||||
}
|
||||
|
||||
fd_install(fd, sync_file->file);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_file:
|
||||
fput(sync_file->file);
|
||||
err_put_fd:
|
||||
put_unused_fd(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long dma_buf_import_sync_file(struct dma_buf *dmabuf,
|
||||
const void __user *user_data)
|
||||
{
|
||||
struct dma_buf_import_sync_file arg;
|
||||
struct dma_fence *fence;
|
||||
enum dma_resv_usage usage;
|
||||
int ret = 0;
|
||||
|
||||
if (copy_from_user(&arg, user_data, sizeof(arg)))
|
||||
return -EFAULT;
|
||||
|
||||
if (arg.flags & ~DMA_BUF_SYNC_RW)
|
||||
return -EINVAL;
|
||||
|
||||
if ((arg.flags & DMA_BUF_SYNC_RW) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
fence = sync_file_get_fence(arg.fd);
|
||||
if (!fence)
|
||||
return -EINVAL;
|
||||
|
||||
usage = (arg.flags & DMA_BUF_SYNC_WRITE) ? DMA_RESV_USAGE_WRITE :
|
||||
DMA_RESV_USAGE_READ;
|
||||
|
||||
dma_resv_lock(dmabuf->resv, NULL);
|
||||
|
||||
ret = dma_resv_reserve_fences(dmabuf->resv, 1);
|
||||
if (!ret)
|
||||
dma_resv_add_fence(dmabuf->resv, fence, usage);
|
||||
|
||||
dma_resv_unlock(dmabuf->resv);
|
||||
|
||||
dma_fence_put(fence);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static long dma_buf_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
|
@ -369,6 +468,13 @@ static long dma_buf_ioctl(struct file *file,
|
|||
case DMA_BUF_SET_NAME_B:
|
||||
return dma_buf_set_name(dmabuf, (const char __user *)arg);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SYNC_FILE)
|
||||
case DMA_BUF_IOCTL_EXPORT_SYNC_FILE:
|
||||
return dma_buf_export_sync_file(dmabuf, (void __user *)arg);
|
||||
case DMA_BUF_IOCTL_IMPORT_SYNC_FILE:
|
||||
return dma_buf_import_sync_file(dmabuf, (const void __user *)arg);
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
|
|
@ -365,7 +365,23 @@ static struct miscdevice udmabuf_misc = {
|
|||
|
||||
static int __init udmabuf_dev_init(void)
|
||||
{
|
||||
return misc_register(&udmabuf_misc);
|
||||
int ret;
|
||||
|
||||
ret = misc_register(&udmabuf_misc);
|
||||
if (ret < 0) {
|
||||
pr_err("Could not initialize udmabuf device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dma_coerce_mask_and_coherent(udmabuf_misc.this_device,
|
||||
DMA_BIT_MASK(64));
|
||||
if (ret < 0) {
|
||||
pr_err("Could not setup DMA mask for udmabuf device\n");
|
||||
misc_deregister(&udmabuf_misc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit udmabuf_dev_exit(void)
|
||||
|
|
|
@ -34,21 +34,59 @@
|
|||
#include <linux/screen_info.h>
|
||||
#include <linux/sysfb.h>
|
||||
|
||||
static struct platform_device *pd;
|
||||
static DEFINE_MUTEX(disable_lock);
|
||||
static bool disabled;
|
||||
|
||||
static bool sysfb_unregister(void)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(pd))
|
||||
return false;
|
||||
|
||||
platform_device_unregister(pd);
|
||||
pd = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfb_disable() - disable the Generic System Framebuffers support
|
||||
*
|
||||
* This disables the registration of system framebuffer devices that match the
|
||||
* generic drivers that make use of the system framebuffer set up by firmware.
|
||||
*
|
||||
* It also unregisters a device if this was already registered by sysfb_init().
|
||||
*
|
||||
* Context: The function can sleep. A @disable_lock mutex is acquired to serialize
|
||||
* against sysfb_init(), that registers a system framebuffer device.
|
||||
*/
|
||||
void sysfb_disable(void)
|
||||
{
|
||||
mutex_lock(&disable_lock);
|
||||
sysfb_unregister();
|
||||
disabled = true;
|
||||
mutex_unlock(&disable_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sysfb_disable);
|
||||
|
||||
static __init int sysfb_init(void)
|
||||
{
|
||||
struct screen_info *si = &screen_info;
|
||||
struct simplefb_platform_data mode;
|
||||
struct platform_device *pd;
|
||||
const char *name;
|
||||
bool compatible;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&disable_lock);
|
||||
if (disabled)
|
||||
goto unlock_mutex;
|
||||
|
||||
/* try to create a simple-framebuffer device */
|
||||
compatible = sysfb_parse_mode(si, &mode);
|
||||
if (compatible) {
|
||||
ret = sysfb_create_simplefb(si, &mode);
|
||||
if (!ret)
|
||||
return 0;
|
||||
pd = sysfb_create_simplefb(si, &mode);
|
||||
if (!IS_ERR(pd))
|
||||
goto unlock_mutex;
|
||||
}
|
||||
|
||||
/* if the FB is incompatible, create a legacy framebuffer device */
|
||||
|
@ -60,8 +98,10 @@ static __init int sysfb_init(void)
|
|||
name = "platform-framebuffer";
|
||||
|
||||
pd = platform_device_alloc(name, 0);
|
||||
if (!pd)
|
||||
return -ENOMEM;
|
||||
if (!pd) {
|
||||
ret = -ENOMEM;
|
||||
goto unlock_mutex;
|
||||
}
|
||||
|
||||
sysfb_apply_efi_quirks(pd);
|
||||
|
||||
|
@ -73,9 +113,11 @@ static __init int sysfb_init(void)
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
goto unlock_mutex;
|
||||
err:
|
||||
platform_device_put(pd);
|
||||
unlock_mutex:
|
||||
mutex_unlock(&disable_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,8 +57,8 @@ __init bool sysfb_parse_mode(const struct screen_info *si,
|
|||
return false;
|
||||
}
|
||||
|
||||
__init int sysfb_create_simplefb(const struct screen_info *si,
|
||||
const struct simplefb_platform_data *mode)
|
||||
__init struct platform_device *sysfb_create_simplefb(const struct screen_info *si,
|
||||
const struct simplefb_platform_data *mode)
|
||||
{
|
||||
struct platform_device *pd;
|
||||
struct resource res;
|
||||
|
@ -76,7 +76,7 @@ __init int sysfb_create_simplefb(const struct screen_info *si,
|
|||
base |= (u64)si->ext_lfb_base << 32;
|
||||
if (!base || (u64)(resource_size_t)base != base) {
|
||||
printk(KERN_DEBUG "sysfb: inaccessible VRAM base\n");
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -93,7 +93,7 @@ __init int sysfb_create_simplefb(const struct screen_info *si,
|
|||
length = mode->height * mode->stride;
|
||||
if (length > size) {
|
||||
printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n");
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
length = PAGE_ALIGN(length);
|
||||
|
||||
|
@ -104,11 +104,11 @@ __init int sysfb_create_simplefb(const struct screen_info *si,
|
|||
res.start = base;
|
||||
res.end = res.start + length - 1;
|
||||
if (res.end <= res.start)
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pd = platform_device_alloc("simple-framebuffer", 0);
|
||||
if (!pd)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
sysfb_apply_efi_quirks(pd);
|
||||
|
||||
|
@ -124,10 +124,10 @@ __init int sysfb_create_simplefb(const struct screen_info *si,
|
|||
if (ret)
|
||||
goto err_put_device;
|
||||
|
||||
return 0;
|
||||
return pd;
|
||||
|
||||
err_put_device:
|
||||
platform_device_put(pd);
|
||||
|
||||
return ret;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
|
|
@ -351,6 +351,8 @@ source "drivers/gpu/drm/etnaviv/Kconfig"
|
|||
|
||||
source "drivers/gpu/drm/hisilicon/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/logicvc/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/mediatek/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/mxsfb/Kconfig"
|
||||
|
|
|
@ -121,6 +121,7 @@ obj-$(CONFIG_DRM_STM) += stm/
|
|||
obj-$(CONFIG_DRM_STI) += sti/
|
||||
obj-y += imx/
|
||||
obj-$(CONFIG_DRM_INGENIC) += ingenic/
|
||||
obj-$(CONFIG_DRM_LOGICVC) += logicvc/
|
||||
obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
|
||||
obj-$(CONFIG_DRM_MESON) += meson/
|
||||
obj-y += i2c/
|
||||
|
|
|
@ -6592,12 +6592,14 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)
|
|||
return &state->base;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc)
|
||||
{
|
||||
crtc_debugfs_init(crtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int dm_set_vupdate_irq(struct drm_crtc *crtc, bool enable)
|
||||
{
|
||||
|
@ -6691,7 +6693,9 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
|
|||
.enable_vblank = dm_enable_vblank,
|
||||
.disable_vblank = dm_disable_vblank,
|
||||
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
.late_register = amdgpu_dm_crtc_late_register,
|
||||
#endif
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
|
|
|
@ -94,6 +94,8 @@ config DRM_ITE_IT6505
|
|||
select DRM_KMS_HELPER
|
||||
select DRM_DP_HELPER
|
||||
select EXTCON
|
||||
select CRYPTO
|
||||
select CRYPTO_HASH
|
||||
help
|
||||
ITE IT6505 DisplayPort bridge chip driver.
|
||||
|
||||
|
|
|
@ -1065,6 +1065,10 @@ static int adv7511_init_cec_regmap(struct adv7511 *adv)
|
|||
ADV7511_CEC_I2C_ADDR_DEFAULT);
|
||||
if (IS_ERR(adv->i2c_cec))
|
||||
return PTR_ERR(adv->i2c_cec);
|
||||
|
||||
regmap_write(adv->regmap, ADV7511_REG_CEC_I2C_ADDR,
|
||||
adv->i2c_cec->addr << 1);
|
||||
|
||||
i2c_set_clientdata(adv->i2c_cec, adv);
|
||||
|
||||
adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
|
||||
|
@ -1271,9 +1275,6 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
|||
if (ret)
|
||||
goto err_i2c_unregister_packet;
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR,
|
||||
adv7511->i2c_cec->addr << 1);
|
||||
|
||||
INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
|
||||
|
||||
if (i2c->irq) {
|
||||
|
|
|
@ -1637,16 +1637,16 @@ static int anx7625_parse_dt(struct device *dev,
|
|||
if (of_property_read_u32(ep0, "bus-type", &bus_type))
|
||||
bus_type = 0;
|
||||
|
||||
mipi_lanes = of_property_count_u32_elems(ep0, "data-lanes");
|
||||
mipi_lanes = drm_of_get_data_lanes_count(ep0, 1, MAX_LANES_SUPPORT);
|
||||
of_node_put(ep0);
|
||||
}
|
||||
|
||||
if (bus_type == V4L2_FWNODE_BUS_TYPE_PARALLEL) /* bus type is Parallel(DSI) */
|
||||
pdata->is_dpi = 0;
|
||||
|
||||
pdata->mipi_lanes = mipi_lanes;
|
||||
if (pdata->mipi_lanes > MAX_LANES_SUPPORT || pdata->mipi_lanes <= 0)
|
||||
pdata->mipi_lanes = MAX_LANES_SUPPORT;
|
||||
pdata->mipi_lanes = MAX_LANES_SUPPORT;
|
||||
if (mipi_lanes > 0)
|
||||
pdata->mipi_lanes = mipi_lanes;
|
||||
|
||||
if (pdata->is_dpi)
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "found MIPI DPI host node.\n");
|
||||
|
@ -1658,8 +1658,10 @@ static int anx7625_parse_dt(struct device *dev,
|
|||
|
||||
pdata->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
|
||||
if (IS_ERR(pdata->panel_bridge)) {
|
||||
if (PTR_ERR(pdata->panel_bridge) == -ENODEV)
|
||||
if (PTR_ERR(pdata->panel_bridge) == -ENODEV) {
|
||||
pdata->panel_bridge = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PTR_ERR(pdata->panel_bridge);
|
||||
}
|
||||
|
|
|
@ -498,21 +498,18 @@ static int chipone_dsi_attach(struct chipone *icn)
|
|||
{
|
||||
struct mipi_dsi_device *dsi = icn->dsi;
|
||||
struct device *dev = icn->dev;
|
||||
struct device_node *endpoint;
|
||||
int dsi_lanes, ret;
|
||||
|
||||
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
|
||||
dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
|
||||
of_node_put(endpoint);
|
||||
dsi_lanes = drm_of_get_data_lanes_count_ep(dev->of_node, 0, 0, 1, 4);
|
||||
|
||||
/*
|
||||
* If the 'data-lanes' property does not exist in DT or is invalid,
|
||||
* default to previously hard-coded behavior, which was 4 data lanes.
|
||||
*/
|
||||
if (dsi_lanes >= 1 && dsi_lanes <= 4)
|
||||
icn->dsi->lanes = dsi_lanes;
|
||||
else
|
||||
if (dsi_lanes < 0)
|
||||
icn->dsi->lanes = 4;
|
||||
else
|
||||
icn->dsi->lanes = dsi_lanes;
|
||||
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
|
||||
|
|
|
@ -607,7 +607,6 @@ static int lt8912_parse_dt(struct lt8912 *lt)
|
|||
int ret;
|
||||
int data_lanes;
|
||||
struct device_node *port_node;
|
||||
struct device_node *endpoint;
|
||||
|
||||
gp_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gp_reset)) {
|
||||
|
@ -618,16 +617,12 @@ static int lt8912_parse_dt(struct lt8912 *lt)
|
|||
}
|
||||
lt->gp_reset = gp_reset;
|
||||
|
||||
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
|
||||
if (!endpoint)
|
||||
return -ENODEV;
|
||||
|
||||
data_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
|
||||
of_node_put(endpoint);
|
||||
data_lanes = drm_of_get_data_lanes_count_ep(dev->of_node, 0, -1, 1, 4);
|
||||
if (data_lanes < 0) {
|
||||
dev_err(lt->dev, "%s: Bad data-lanes property\n", __func__);
|
||||
return data_lanes;
|
||||
}
|
||||
|
||||
lt->data_lanes = data_lanes;
|
||||
|
||||
lt->host_node = of_graph_get_remote_node(dev->of_node, 0, -1);
|
||||
|
|
|
@ -686,7 +686,7 @@ static int lt9211_host_attach(struct lt9211 *ctx)
|
|||
int ret;
|
||||
|
||||
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
|
||||
dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
|
||||
dsi_lanes = drm_of_get_data_lanes_count(endpoint, 1, 4);
|
||||
host_node = of_graph_get_remote_port_parent(endpoint);
|
||||
host = of_find_mipi_dsi_host_by_node(host_node);
|
||||
of_node_put(host_node);
|
||||
|
@ -695,8 +695,8 @@ static int lt9211_host_attach(struct lt9211 *ctx)
|
|||
if (!host)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (dsi_lanes < 0 || dsi_lanes > 4)
|
||||
return -EINVAL;
|
||||
if (dsi_lanes < 0)
|
||||
return dsi_lanes;
|
||||
|
||||
dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
|
||||
if (IS_ERR(dsi))
|
||||
|
|
|
@ -1890,18 +1890,18 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc)
|
|||
int dsi_lanes, ret;
|
||||
|
||||
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
|
||||
dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
|
||||
dsi_lanes = drm_of_get_data_lanes_count(endpoint, 1, 4);
|
||||
host_node = of_graph_get_remote_port_parent(endpoint);
|
||||
host = of_find_mipi_dsi_host_by_node(host_node);
|
||||
of_node_put(host_node);
|
||||
of_node_put(endpoint);
|
||||
|
||||
if (dsi_lanes <= 0 || dsi_lanes > 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (!host)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (dsi_lanes < 0)
|
||||
return dsi_lanes;
|
||||
|
||||
dsi = mipi_dsi_device_register_full(host, &info);
|
||||
if (IS_ERR(dsi))
|
||||
return dev_err_probe(dev, PTR_ERR(dsi),
|
||||
|
|
|
@ -529,8 +529,7 @@ static int tc358775_parse_dt(struct device_node *np, struct tc_data *tc)
|
|||
struct device_node *endpoint;
|
||||
struct device_node *parent;
|
||||
struct device_node *remote;
|
||||
struct property *prop;
|
||||
int len = 0;
|
||||
int dsi_lanes = -1;
|
||||
|
||||
/*
|
||||
* To get the data-lanes of dsi, we need to access the dsi0_out of port1
|
||||
|
@ -544,25 +543,15 @@ static int tc358775_parse_dt(struct device_node *np, struct tc_data *tc)
|
|||
of_node_put(endpoint);
|
||||
if (parent) {
|
||||
/* dsi0 port 1 */
|
||||
endpoint = of_graph_get_endpoint_by_regs(parent, 1, -1);
|
||||
dsi_lanes = drm_of_get_data_lanes_count_ep(parent, 1, -1, 1, 4);
|
||||
of_node_put(parent);
|
||||
if (endpoint) {
|
||||
prop = of_find_property(endpoint, "data-lanes",
|
||||
&len);
|
||||
of_node_put(endpoint);
|
||||
if (!prop) {
|
||||
dev_err(tc->dev,
|
||||
"failed to find data lane\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tc->num_dsi_lanes = len / sizeof(u32);
|
||||
if (dsi_lanes < 0)
|
||||
return dsi_lanes;
|
||||
|
||||
if (tc->num_dsi_lanes < 1 || tc->num_dsi_lanes > 4)
|
||||
return -EINVAL;
|
||||
tc->num_dsi_lanes = dsi_lanes;
|
||||
|
||||
tc->host_node = of_graph_get_remote_node(np, 0, 0);
|
||||
if (!tc->host_node)
|
||||
|
|
|
@ -140,12 +140,10 @@ struct sn65dsi83 {
|
|||
struct drm_bridge bridge;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct device_node *host_node;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct gpio_desc *enable_gpio;
|
||||
struct regulator *vcc;
|
||||
int dsi_lanes;
|
||||
bool lvds_dual_link;
|
||||
bool lvds_dual_link_even_odd_swap;
|
||||
};
|
||||
|
@ -306,7 +304,7 @@ static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx,
|
|||
*/
|
||||
return DIV_ROUND_UP(clamp((unsigned int)mode->clock *
|
||||
mipi_dsi_pixel_format_to_bpp(ctx->dsi->format) /
|
||||
ctx->dsi_lanes / 2, 40000U, 500000U), 5000U);
|
||||
ctx->dsi->lanes / 2, 40000U, 500000U), 5000U);
|
||||
}
|
||||
|
||||
static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx)
|
||||
|
@ -314,7 +312,7 @@ static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx)
|
|||
/* The divider is (DSI_CLK / LVDS_CLK) - 1, which really is: */
|
||||
unsigned int dsi_div = mipi_dsi_pixel_format_to_bpp(ctx->dsi->format);
|
||||
|
||||
dsi_div /= ctx->dsi_lanes;
|
||||
dsi_div /= ctx->dsi->lanes;
|
||||
|
||||
if (!ctx->lvds_dual_link)
|
||||
dsi_div /= 2;
|
||||
|
@ -405,7 +403,7 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
|
|||
/* Set number of DSI lanes and LVDS link config. */
|
||||
regmap_write(ctx->regmap, REG_DSI_LANE,
|
||||
REG_DSI_LANE_DSI_CHANNEL_MODE_SINGLE |
|
||||
REG_DSI_LANE_CHA_DSI_LANES(~(ctx->dsi_lanes - 1)) |
|
||||
REG_DSI_LANE_CHA_DSI_LANES(~(ctx->dsi->lanes - 1)) |
|
||||
/* CHB is DSI85-only, set to default on DSI83/DSI84 */
|
||||
REG_DSI_LANE_CHB_DSI_LANES(3));
|
||||
/* No equalization. */
|
||||
|
@ -569,22 +567,6 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
|
|||
{
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct device *dev = ctx->dev;
|
||||
struct device_node *endpoint;
|
||||
int ret;
|
||||
|
||||
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
|
||||
ctx->dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
|
||||
ctx->host_node = of_graph_get_remote_port_parent(endpoint);
|
||||
of_node_put(endpoint);
|
||||
|
||||
if (ctx->dsi_lanes <= 0 || ctx->dsi_lanes > 4) {
|
||||
ret = -EINVAL;
|
||||
goto err_put_node;
|
||||
}
|
||||
if (!ctx->host_node) {
|
||||
ret = -ENODEV;
|
||||
goto err_put_node;
|
||||
}
|
||||
|
||||
ctx->lvds_dual_link = false;
|
||||
ctx->lvds_dual_link_even_odd_swap = false;
|
||||
|
@ -610,10 +592,8 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
|
|||
}
|
||||
|
||||
panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 2, 0);
|
||||
if (IS_ERR(panel_bridge)) {
|
||||
ret = PTR_ERR(panel_bridge);
|
||||
goto err_put_node;
|
||||
}
|
||||
if (IS_ERR(panel_bridge))
|
||||
return PTR_ERR(panel_bridge);
|
||||
|
||||
ctx->panel_bridge = panel_bridge;
|
||||
|
||||
|
@ -623,15 +603,13 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
|
|||
"Failed to get supply 'vcc'\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_node:
|
||||
of_node_put(ctx->host_node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sn65dsi83_host_attach(struct sn65dsi83 *ctx)
|
||||
{
|
||||
struct device *dev = ctx->dev;
|
||||
struct device_node *host_node;
|
||||
struct device_node *endpoint;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct mipi_dsi_host *host;
|
||||
const struct mipi_dsi_device_info info = {
|
||||
|
@ -639,13 +617,20 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx)
|
|||
.channel = 0,
|
||||
.node = NULL,
|
||||
};
|
||||
int ret;
|
||||
int dsi_lanes, ret;
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(ctx->host_node);
|
||||
if (!host) {
|
||||
dev_err(dev, "failed to find dsi host\n");
|
||||
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
|
||||
dsi_lanes = drm_of_get_data_lanes_count(endpoint, 1, 4);
|
||||
host_node = of_graph_get_remote_port_parent(endpoint);
|
||||
host = of_find_mipi_dsi_host_by_node(host_node);
|
||||
of_node_put(host_node);
|
||||
of_node_put(endpoint);
|
||||
|
||||
if (!host)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
if (dsi_lanes < 0)
|
||||
return dsi_lanes;
|
||||
|
||||
dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
|
||||
if (IS_ERR(dsi))
|
||||
|
@ -654,7 +639,7 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx)
|
|||
|
||||
ctx->dsi = dsi;
|
||||
|
||||
dsi->lanes = ctx->dsi_lanes;
|
||||
dsi->lanes = dsi_lanes;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST;
|
||||
|
||||
|
@ -701,10 +686,8 @@ static int sn65dsi83_probe(struct i2c_client *client,
|
|||
return ret;
|
||||
|
||||
ctx->regmap = devm_regmap_init_i2c(client, &sn65dsi83_regmap_config);
|
||||
if (IS_ERR(ctx->regmap)) {
|
||||
ret = PTR_ERR(ctx->regmap);
|
||||
goto err_put_node;
|
||||
}
|
||||
if (IS_ERR(ctx->regmap))
|
||||
return PTR_ERR(ctx->regmap);
|
||||
|
||||
dev_set_drvdata(dev, ctx);
|
||||
i2c_set_clientdata(client, ctx);
|
||||
|
@ -721,8 +704,6 @@ static int sn65dsi83_probe(struct i2c_client *client,
|
|||
|
||||
err_remove_bridge:
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
err_put_node:
|
||||
of_node_put(ctx->host_node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -731,7 +712,6 @@ static int sn65dsi83_remove(struct i2c_client *client)
|
|||
struct sn65dsi83 *ctx = i2c_get_clientdata(client);
|
||||
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
of_node_put(ctx->host_node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1142,8 +1142,8 @@ static void ti_sn_bridge_parse_lanes(struct ti_sn65dsi86 *pdata,
|
|||
* mappings that the hardware supports.
|
||||
*/
|
||||
endpoint = of_graph_get_endpoint_by_regs(np, 1, -1);
|
||||
dp_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
|
||||
if (dp_lanes > 0 && dp_lanes <= SN_MAX_DP_LANES) {
|
||||
dp_lanes = drm_of_get_data_lanes_count(endpoint, 1, SN_MAX_DP_LANES);
|
||||
if (dp_lanes > 0) {
|
||||
of_property_read_u32_array(endpoint, "data-lanes",
|
||||
lane_assignments, dp_lanes);
|
||||
of_property_read_u32_array(endpoint, "lane-polarities",
|
||||
|
|
|
@ -3860,9 +3860,7 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
|
|||
if (!mgr->mst_primary)
|
||||
goto out_fail;
|
||||
|
||||
ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd,
|
||||
DP_RECEIVER_CAP_SIZE);
|
||||
if (ret != DP_RECEIVER_CAP_SIZE) {
|
||||
if (drm_dp_read_dpcd_caps(mgr->aux, mgr->dpcd) < 0) {
|
||||
drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
|
||||
goto out_fail;
|
||||
}
|
||||
|
@ -4911,8 +4909,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
|
|||
u8 buf[DP_PAYLOAD_TABLE_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE);
|
||||
if (ret) {
|
||||
if (drm_dp_read_dpcd_caps(mgr->aux, buf) < 0) {
|
||||
seq_printf(m, "dpcd read failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -1613,6 +1613,35 @@ static const void *edid_extension_block_data(const struct edid *edid, int index)
|
|||
return edid_block_data(edid, index + 1);
|
||||
}
|
||||
|
||||
static int drm_edid_block_count(const struct drm_edid *drm_edid)
|
||||
{
|
||||
int num_blocks;
|
||||
|
||||
/* Starting point */
|
||||
num_blocks = edid_block_count(drm_edid->edid);
|
||||
|
||||
/* Limit by allocated size */
|
||||
num_blocks = min(num_blocks, (int)drm_edid->size / EDID_LENGTH);
|
||||
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
static int drm_edid_extension_block_count(const struct drm_edid *drm_edid)
|
||||
{
|
||||
return drm_edid_block_count(drm_edid) - 1;
|
||||
}
|
||||
|
||||
static const void *drm_edid_block_data(const struct drm_edid *drm_edid, int index)
|
||||
{
|
||||
return edid_block_data(drm_edid->edid, index);
|
||||
}
|
||||
|
||||
static const void *drm_edid_extension_block_data(const struct drm_edid *drm_edid,
|
||||
int index)
|
||||
{
|
||||
return edid_extension_block_data(drm_edid->edid, index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializer helper for legacy interfaces, where we have no choice but to
|
||||
* trust edid size. Not for general purpose use.
|
||||
|
@ -1665,8 +1694,8 @@ static const void *__drm_edid_iter_next(struct drm_edid_iter *iter)
|
|||
if (!iter->drm_edid)
|
||||
return NULL;
|
||||
|
||||
if (iter->index < edid_block_count(iter->drm_edid->edid))
|
||||
block = edid_block_data(iter->drm_edid->edid, iter->index++);
|
||||
if (iter->index < drm_edid_block_count(iter->drm_edid))
|
||||
block = drm_edid_block_data(iter->drm_edid, iter->index++);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
@ -1992,13 +2021,16 @@ bool drm_edid_is_valid(struct edid *edid)
|
|||
EXPORT_SYMBOL(drm_edid_is_valid);
|
||||
|
||||
static struct edid *edid_filter_invalid_blocks(const struct edid *edid,
|
||||
int invalid_blocks)
|
||||
int invalid_blocks,
|
||||
size_t *alloc_size)
|
||||
{
|
||||
struct edid *new, *dest_block;
|
||||
int valid_extensions = edid->extensions - invalid_blocks;
|
||||
int i;
|
||||
|
||||
new = kmalloc(edid_size_by_blocks(valid_extensions + 1), GFP_KERNEL);
|
||||
*alloc_size = edid_size_by_blocks(valid_extensions + 1);
|
||||
|
||||
new = kmalloc(*alloc_size, GFP_KERNEL);
|
||||
if (!new)
|
||||
goto out;
|
||||
|
||||
|
@ -2111,7 +2143,8 @@ static void connector_bad_edid(struct drm_connector *connector,
|
|||
}
|
||||
|
||||
/* Get override or firmware EDID */
|
||||
static struct edid *drm_get_override_edid(struct drm_connector *connector)
|
||||
static struct edid *drm_get_override_edid(struct drm_connector *connector,
|
||||
size_t *alloc_size)
|
||||
{
|
||||
struct edid *override = NULL;
|
||||
|
||||
|
@ -2121,6 +2154,10 @@ static struct edid *drm_get_override_edid(struct drm_connector *connector)
|
|||
if (!override)
|
||||
override = drm_load_edid_firmware(connector);
|
||||
|
||||
/* FIXME: Get alloc size from deeper down the stack */
|
||||
if (!IS_ERR_OR_NULL(override) && alloc_size)
|
||||
*alloc_size = edid_size(override);
|
||||
|
||||
return IS_ERR(override) ? NULL : override;
|
||||
}
|
||||
|
||||
|
@ -2140,7 +2177,7 @@ int drm_add_override_edid_modes(struct drm_connector *connector)
|
|||
struct edid *override;
|
||||
int num_modes = 0;
|
||||
|
||||
override = drm_get_override_edid(connector);
|
||||
override = drm_get_override_edid(connector, NULL);
|
||||
if (override) {
|
||||
drm_connector_update_edid_property(connector, override);
|
||||
num_modes = drm_add_edid_modes(connector, override);
|
||||
|
@ -2189,39 +2226,20 @@ static enum edid_block_status edid_block_read(void *block, unsigned int block_nu
|
|||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_do_get_edid - get EDID data using a custom EDID block read function
|
||||
* @connector: connector we're probing
|
||||
* @read_block: EDID block read function
|
||||
* @context: private data passed to the block read function
|
||||
*
|
||||
* When the I2C adapter connected to the DDC bus is hidden behind a device that
|
||||
* exposes a different interface to read EDID blocks this function can be used
|
||||
* to get EDID data using a custom block read function.
|
||||
*
|
||||
* As in the general case the DDC bus is accessible by the kernel at the I2C
|
||||
* level, drivers must make all reasonable efforts to expose it as an I2C
|
||||
* adapter and use drm_get_edid() instead of abusing this function.
|
||||
*
|
||||
* The EDID may be overridden using debugfs override_edid or firmware EDID
|
||||
* (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority
|
||||
* order. Having either of them bypasses actual EDID reads.
|
||||
*
|
||||
* Return: Pointer to valid EDID or NULL if we couldn't find any.
|
||||
*/
|
||||
struct edid *drm_do_get_edid(struct drm_connector *connector,
|
||||
read_block_fn read_block,
|
||||
void *context)
|
||||
static struct edid *_drm_do_get_edid(struct drm_connector *connector,
|
||||
read_block_fn read_block, void *context,
|
||||
size_t *size)
|
||||
{
|
||||
enum edid_block_status status;
|
||||
int i, invalid_blocks = 0;
|
||||
struct edid *edid, *new;
|
||||
size_t alloc_size = EDID_LENGTH;
|
||||
|
||||
edid = drm_get_override_edid(connector);
|
||||
edid = drm_get_override_edid(connector, &alloc_size);
|
||||
if (edid)
|
||||
goto ok;
|
||||
|
||||
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
||||
edid = kmalloc(alloc_size, GFP_KERNEL);
|
||||
if (!edid)
|
||||
return NULL;
|
||||
|
||||
|
@ -2249,7 +2267,8 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
|
|||
if (!edid_extension_block_count(edid))
|
||||
goto ok;
|
||||
|
||||
new = krealloc(edid, edid_size(edid), GFP_KERNEL);
|
||||
alloc_size = edid_size(edid);
|
||||
new = krealloc(edid, alloc_size, GFP_KERNEL);
|
||||
if (!new)
|
||||
goto fail;
|
||||
edid = new;
|
||||
|
@ -2271,18 +2290,130 @@ struct edid *drm_do_get_edid(struct drm_connector *connector,
|
|||
if (invalid_blocks) {
|
||||
connector_bad_edid(connector, edid, edid_block_count(edid));
|
||||
|
||||
edid = edid_filter_invalid_blocks(edid, invalid_blocks);
|
||||
edid = edid_filter_invalid_blocks(edid, invalid_blocks,
|
||||
&alloc_size);
|
||||
}
|
||||
|
||||
ok:
|
||||
if (size)
|
||||
*size = alloc_size;
|
||||
|
||||
return edid;
|
||||
|
||||
fail:
|
||||
kfree(edid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_do_get_edid - get EDID data using a custom EDID block read function
|
||||
* @connector: connector we're probing
|
||||
* @read_block: EDID block read function
|
||||
* @context: private data passed to the block read function
|
||||
*
|
||||
* When the I2C adapter connected to the DDC bus is hidden behind a device that
|
||||
* exposes a different interface to read EDID blocks this function can be used
|
||||
* to get EDID data using a custom block read function.
|
||||
*
|
||||
* As in the general case the DDC bus is accessible by the kernel at the I2C
|
||||
* level, drivers must make all reasonable efforts to expose it as an I2C
|
||||
* adapter and use drm_get_edid() instead of abusing this function.
|
||||
*
|
||||
* The EDID may be overridden using debugfs override_edid or firmware EDID
|
||||
* (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority
|
||||
* order. Having either of them bypasses actual EDID reads.
|
||||
*
|
||||
* Return: Pointer to valid EDID or NULL if we couldn't find any.
|
||||
*/
|
||||
struct edid *drm_do_get_edid(struct drm_connector *connector,
|
||||
read_block_fn read_block,
|
||||
void *context)
|
||||
{
|
||||
return _drm_do_get_edid(connector, read_block, context, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_do_get_edid);
|
||||
|
||||
/* Allocate struct drm_edid container *without* duplicating the edid data */
|
||||
static const struct drm_edid *_drm_edid_alloc(const void *edid, size_t size)
|
||||
{
|
||||
struct drm_edid *drm_edid;
|
||||
|
||||
if (!edid || !size || size < EDID_LENGTH)
|
||||
return NULL;
|
||||
|
||||
drm_edid = kzalloc(sizeof(*drm_edid), GFP_KERNEL);
|
||||
if (drm_edid) {
|
||||
drm_edid->edid = edid;
|
||||
drm_edid->size = size;
|
||||
}
|
||||
|
||||
return drm_edid;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_edid_alloc - Allocate a new drm_edid container
|
||||
* @edid: Pointer to raw EDID data
|
||||
* @size: Size of memory allocated for EDID
|
||||
*
|
||||
* Allocate a new drm_edid container. Do not calculate edid size from edid, pass
|
||||
* the actual size that has been allocated for the data. There is no validation
|
||||
* of the raw EDID data against the size, but at least the EDID base block must
|
||||
* fit in the buffer.
|
||||
*
|
||||
* The returned pointer must be freed using drm_edid_free().
|
||||
*
|
||||
* Return: drm_edid container, or NULL on errors
|
||||
*/
|
||||
const struct drm_edid *drm_edid_alloc(const void *edid, size_t size)
|
||||
{
|
||||
const struct drm_edid *drm_edid;
|
||||
|
||||
if (!edid || !size || size < EDID_LENGTH)
|
||||
return NULL;
|
||||
|
||||
edid = kmemdup(edid, size, GFP_KERNEL);
|
||||
if (!edid)
|
||||
return NULL;
|
||||
|
||||
drm_edid = _drm_edid_alloc(edid, size);
|
||||
if (!drm_edid)
|
||||
kfree(edid);
|
||||
|
||||
return drm_edid;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_alloc);
|
||||
|
||||
/**
|
||||
* drm_edid_dup - Duplicate a drm_edid container
|
||||
* @drm_edid: EDID to duplicate
|
||||
*
|
||||
* The returned pointer must be freed using drm_edid_free().
|
||||
*
|
||||
* Returns: drm_edid container copy, or NULL on errors
|
||||
*/
|
||||
const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid)
|
||||
{
|
||||
if (!drm_edid)
|
||||
return NULL;
|
||||
|
||||
return drm_edid_alloc(drm_edid->edid, drm_edid->size);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_dup);
|
||||
|
||||
/**
|
||||
* drm_edid_free - Free the drm_edid container
|
||||
* @drm_edid: EDID to free
|
||||
*/
|
||||
void drm_edid_free(const struct drm_edid *drm_edid)
|
||||
{
|
||||
if (!drm_edid)
|
||||
return;
|
||||
|
||||
kfree(drm_edid->edid);
|
||||
kfree(drm_edid);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_free);
|
||||
|
||||
/**
|
||||
* drm_probe_ddc() - probe DDC presence
|
||||
* @adapter: I2C adapter to probe
|
||||
|
@ -2319,12 +2450,118 @@ struct edid *drm_get_edid(struct drm_connector *connector,
|
|||
if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
|
||||
return NULL;
|
||||
|
||||
edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
|
||||
edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL);
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
return edid;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_get_edid);
|
||||
|
||||
/**
|
||||
* drm_edid_read_custom - Read EDID data using given EDID block read function
|
||||
* @connector: Connector to use
|
||||
* @read_block: EDID block read function
|
||||
* @context: Private data passed to the block read function
|
||||
*
|
||||
* When the I2C adapter connected to the DDC bus is hidden behind a device that
|
||||
* exposes a different interface to read EDID blocks this function can be used
|
||||
* to get EDID data using a custom block read function.
|
||||
*
|
||||
* As in the general case the DDC bus is accessible by the kernel at the I2C
|
||||
* level, drivers must make all reasonable efforts to expose it as an I2C
|
||||
* adapter and use drm_edid_read() or drm_edid_read_ddc() instead of abusing
|
||||
* this function.
|
||||
*
|
||||
* The EDID may be overridden using debugfs override_edid or firmware EDID
|
||||
* (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority
|
||||
* order. Having either of them bypasses actual EDID reads.
|
||||
*
|
||||
* The returned pointer must be freed using drm_edid_free().
|
||||
*
|
||||
* Return: Pointer to EDID, or NULL if probe/read failed.
|
||||
*/
|
||||
const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector,
|
||||
read_block_fn read_block,
|
||||
void *context)
|
||||
{
|
||||
const struct drm_edid *drm_edid;
|
||||
struct edid *edid;
|
||||
size_t size = 0;
|
||||
|
||||
edid = _drm_do_get_edid(connector, read_block, context, &size);
|
||||
if (!edid)
|
||||
return NULL;
|
||||
|
||||
/* Sanity check for now */
|
||||
drm_WARN_ON(connector->dev, !size);
|
||||
|
||||
drm_edid = _drm_edid_alloc(edid, size);
|
||||
if (!drm_edid)
|
||||
kfree(edid);
|
||||
|
||||
return drm_edid;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_read_custom);
|
||||
|
||||
/**
|
||||
* drm_edid_read_ddc - Read EDID data using given I2C adapter
|
||||
* @connector: Connector to use
|
||||
* @adapter: I2C adapter to use for DDC
|
||||
*
|
||||
* Read EDID using the given I2C adapter.
|
||||
*
|
||||
* The EDID may be overridden using debugfs override_edid or firmware EDID
|
||||
* (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority
|
||||
* order. Having either of them bypasses actual EDID reads.
|
||||
*
|
||||
* Prefer initializing connector->ddc with drm_connector_init_with_ddc() and
|
||||
* using drm_edid_read() instead of this function.
|
||||
*
|
||||
* The returned pointer must be freed using drm_edid_free().
|
||||
*
|
||||
* Return: Pointer to EDID, or NULL if probe/read failed.
|
||||
*/
|
||||
const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector,
|
||||
struct i2c_adapter *adapter)
|
||||
{
|
||||
const struct drm_edid *drm_edid;
|
||||
|
||||
if (connector->force == DRM_FORCE_OFF)
|
||||
return NULL;
|
||||
|
||||
if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
|
||||
return NULL;
|
||||
|
||||
drm_edid = drm_edid_read_custom(connector, drm_do_probe_ddc_edid, adapter);
|
||||
|
||||
/* Note: Do *not* call connector updates here. */
|
||||
|
||||
return drm_edid;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_read_ddc);
|
||||
|
||||
/**
|
||||
* drm_edid_read - Read EDID data using connector's I2C adapter
|
||||
* @connector: Connector to use
|
||||
*
|
||||
* Read EDID using the connector's I2C adapter.
|
||||
*
|
||||
* The EDID may be overridden using debugfs override_edid or firmware EDID
|
||||
* (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority
|
||||
* order. Having either of them bypasses actual EDID reads.
|
||||
*
|
||||
* The returned pointer must be freed using drm_edid_free().
|
||||
*
|
||||
* Return: Pointer to EDID, or NULL if probe/read failed.
|
||||
*/
|
||||
const struct drm_edid *drm_edid_read(struct drm_connector *connector)
|
||||
{
|
||||
if (drm_WARN_ON(connector->dev, !connector->ddc))
|
||||
return NULL;
|
||||
|
||||
return drm_edid_read_ddc(connector, connector->ddc);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_edid_read);
|
||||
|
||||
static u32 edid_extract_panel_id(const struct edid *edid)
|
||||
{
|
||||
/*
|
||||
|
@ -3574,22 +3811,21 @@ static int add_detailed_modes(struct drm_connector *connector,
|
|||
const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
|
||||
int ext_id, int *ext_index)
|
||||
{
|
||||
const struct edid *edid = drm_edid ? drm_edid->edid : NULL;
|
||||
const u8 *edid_ext = NULL;
|
||||
int i;
|
||||
|
||||
/* No EDID or EDID extensions */
|
||||
if (!edid || !edid_extension_block_count(edid))
|
||||
if (!drm_edid || !drm_edid_extension_block_count(drm_edid))
|
||||
return NULL;
|
||||
|
||||
/* Find CEA extension */
|
||||
for (i = *ext_index; i < edid_extension_block_count(edid); i++) {
|
||||
edid_ext = edid_extension_block_data(edid, i);
|
||||
for (i = *ext_index; i < drm_edid_extension_block_count(drm_edid); i++) {
|
||||
edid_ext = drm_edid_extension_block_data(drm_edid, i);
|
||||
if (edid_block_tag(edid_ext) == ext_id)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= edid_extension_block_count(edid))
|
||||
if (i >= drm_edid_extension_block_count(drm_edid))
|
||||
return NULL;
|
||||
|
||||
*ext_index = i + 1;
|
||||
|
@ -4482,6 +4718,20 @@ __cea_db_iter_current_block(const struct cea_db_iter *iter)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* References:
|
||||
* - CTA-861-H section 7.3.3 CTA Extension Version 3
|
||||
*/
|
||||
static int cea_db_collection_size(const u8 *cta)
|
||||
{
|
||||
u8 d = cta[2];
|
||||
|
||||
if (d < 4 || d > 127)
|
||||
return 0;
|
||||
|
||||
return d - 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* References:
|
||||
* - VESA E-EDID v1.4
|
||||
|
@ -4492,17 +4742,19 @@ static const void *__cea_db_iter_edid_next(struct cea_db_iter *iter)
|
|||
const u8 *ext;
|
||||
|
||||
drm_edid_iter_for_each(ext, &iter->edid_iter) {
|
||||
int size;
|
||||
|
||||
/* Only support CTA Extension revision 3+ */
|
||||
if (ext[0] != CEA_EXT || cea_revision(ext) < 3)
|
||||
continue;
|
||||
|
||||
iter->index = 4;
|
||||
iter->end = ext[2];
|
||||
if (iter->end == 0)
|
||||
iter->end = 127;
|
||||
if (iter->end < 4 || iter->end > 127)
|
||||
size = cea_db_collection_size(ext);
|
||||
if (!size)
|
||||
continue;
|
||||
|
||||
iter->index = 4;
|
||||
iter->end = iter->index + size;
|
||||
|
||||
return ext;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,12 +26,22 @@
|
|||
/**
|
||||
* DOC: cma helpers
|
||||
*
|
||||
* The Contiguous Memory Allocator reserves a pool of memory at early boot
|
||||
* that is used to service requests for large blocks of contiguous memory.
|
||||
* The DRM GEM/CMA helpers are a means to provide buffer objects that are
|
||||
* presented to the device as a contiguous chunk of memory. This is useful
|
||||
* for devices that do not support scatter-gather DMA (either directly or
|
||||
* by using an intimately attached IOMMU).
|
||||
*
|
||||
* The DRM GEM/CMA helpers use this allocator as a means to provide buffer
|
||||
* objects that are physically contiguous in memory. This is useful for
|
||||
* display drivers that are unable to map scattered buffers via an IOMMU.
|
||||
* Despite the name, the DRM GEM/CMA helpers are not hardwired to use the
|
||||
* Contiguous Memory Allocator (CMA).
|
||||
*
|
||||
* For devices that access the memory bus through an (external) IOMMU then
|
||||
* the buffer objects are allocated using a traditional page-based
|
||||
* allocator and may be scattered through physical memory. However they
|
||||
* are contiguous in the IOVA space so appear contiguous to devices using
|
||||
* them.
|
||||
*
|
||||
* For other devices then the helpers rely on CMA to provide buffer
|
||||
* objects that are physically contiguous in memory.
|
||||
*
|
||||
* For GEM callback helpers in struct &drm_gem_object functions, see likewise
|
||||
* named functions with an _object_ infix (e.g., drm_gem_cma_object_vmap() wraps
|
||||
|
@ -111,8 +121,14 @@ error:
|
|||
* @drm: DRM device
|
||||
* @size: size of the object to allocate
|
||||
*
|
||||
* This function creates a CMA GEM object and allocates a contiguous chunk of
|
||||
* memory as backing store.
|
||||
* This function creates a CMA GEM object and allocates memory as backing store.
|
||||
* The allocated memory will occupy a contiguous chunk of bus address space.
|
||||
*
|
||||
* For devices that are directly connected to the memory bus then the allocated
|
||||
* memory will be physically contiguous. For devices that access through an
|
||||
* IOMMU, then the allocated memory is not expected to be physically contiguous
|
||||
* because having contiguous IOVAs is sufficient to meet a devices DMA
|
||||
* requirements.
|
||||
*
|
||||
* Returns:
|
||||
* A struct drm_gem_cma_object * on success or an ERR_PTR()-encoded negative
|
||||
|
@ -162,9 +178,12 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_create);
|
|||
* @size: size of the object to allocate
|
||||
* @handle: return location for the GEM handle
|
||||
*
|
||||
* This function creates a CMA GEM object, allocating a physically contiguous
|
||||
* chunk of memory as backing store. The GEM object is then added to the list
|
||||
* of object associated with the given file and a handle to it is returned.
|
||||
* This function creates a CMA GEM object, allocating a chunk of memory as
|
||||
* backing store. The GEM object is then added to the list of object associated
|
||||
* with the given file and a handle to it is returned.
|
||||
*
|
||||
* The allocated memory will occupy a contiguous chunk of bus address space.
|
||||
* See drm_gem_cma_create() for more details.
|
||||
*
|
||||
* Returns:
|
||||
* A struct drm_gem_cma_object * on success or an ERR_PTR()-encoded negative
|
||||
|
|
|
@ -430,3 +430,64 @@ int drm_of_lvds_get_data_mapping(const struct device_node *port)
|
|||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_of_lvds_get_data_mapping);
|
||||
|
||||
/**
|
||||
* drm_of_get_data_lanes_count - Get DSI/(e)DP data lane count
|
||||
* @endpoint: DT endpoint node of the DSI/(e)DP source or sink
|
||||
* @min: minimum supported number of data lanes
|
||||
* @max: maximum supported number of data lanes
|
||||
*
|
||||
* Count DT "data-lanes" property elements and check for validity.
|
||||
*
|
||||
* Return:
|
||||
* * min..max - positive integer count of "data-lanes" elements
|
||||
* * -ve - the "data-lanes" property is missing or invalid
|
||||
* * -EINVAL - the "data-lanes" property is unsupported
|
||||
*/
|
||||
int drm_of_get_data_lanes_count(const struct device_node *endpoint,
|
||||
const unsigned int min, const unsigned int max)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = of_property_count_u32_elems(endpoint, "data-lanes");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret < min || ret > max)
|
||||
return -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count);
|
||||
|
||||
/**
|
||||
* drm_of_get_data_lanes_count_ep - Get DSI/(e)DP data lane count by endpoint
|
||||
* @port: DT port node of the DSI/(e)DP source or sink
|
||||
* @port_reg: identifier (value of reg property) of the parent port node
|
||||
* @reg: identifier (value of reg property) of the endpoint node
|
||||
* @min: minimum supported number of data lanes
|
||||
* @max: maximum supported number of data lanes
|
||||
*
|
||||
* Count DT "data-lanes" property elements and check for validity.
|
||||
* This variant uses endpoint specifier.
|
||||
*
|
||||
* Return:
|
||||
* * min..max - positive integer count of "data-lanes" elements
|
||||
* * -EINVAL - the "data-mapping" property is unsupported
|
||||
* * -ENODEV - the "data-mapping" property is missing
|
||||
*/
|
||||
int drm_of_get_data_lanes_count_ep(const struct device_node *port,
|
||||
int port_reg, int reg,
|
||||
const unsigned int min,
|
||||
const unsigned int max)
|
||||
{
|
||||
struct device_node *endpoint;
|
||||
int ret;
|
||||
|
||||
endpoint = of_graph_get_endpoint_by_regs(port, port_reg, reg);
|
||||
ret = drm_of_get_data_lanes_count(endpoint, min, max);
|
||||
of_node_put(endpoint);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count_ep);
|
||||
|
|
|
@ -354,6 +354,24 @@ drm_helper_probe_detect(struct drm_connector *connector,
|
|||
}
|
||||
EXPORT_SYMBOL(drm_helper_probe_detect);
|
||||
|
||||
static int drm_helper_probe_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
const struct drm_connector_helper_funcs *connector_funcs =
|
||||
connector->helper_private;
|
||||
int count;
|
||||
|
||||
count = connector_funcs->get_modes(connector);
|
||||
|
||||
/*
|
||||
* Fallback for when DDC probe failed in drm_get_edid() and thus skipped
|
||||
* override/firmware EDID.
|
||||
*/
|
||||
if (count == 0 && connector->status == connector_status_connected)
|
||||
count = drm_add_override_edid_modes(connector);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int __drm_helper_update_and_validate(struct drm_connector *connector,
|
||||
uint32_t maxX, uint32_t maxY,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
|
@ -473,8 +491,6 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
|
|||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_display_mode *mode;
|
||||
const struct drm_connector_helper_funcs *connector_funcs =
|
||||
connector->helper_private;
|
||||
int count = 0, ret;
|
||||
enum drm_connector_status old_status;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
|
@ -559,14 +575,7 @@ retry:
|
|||
goto exit;
|
||||
}
|
||||
|
||||
count = (*connector_funcs->get_modes)(connector);
|
||||
|
||||
/*
|
||||
* Fallback for when DDC probe failed in drm_get_edid() and thus skipped
|
||||
* override/firmware EDID.
|
||||
*/
|
||||
if (count == 0 && connector->status == connector_status_connected)
|
||||
count = drm_add_override_edid_modes(connector);
|
||||
count = drm_helper_probe_get_modes(connector);
|
||||
|
||||
if (count == 0 && (connector->status == connector_status_connected ||
|
||||
connector->status == connector_status_unknown)) {
|
||||
|
|
|
@ -874,8 +874,10 @@ static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
|
|||
|
||||
fence = dma_fence_unwrap_merge(tmp);
|
||||
dma_fence_put(tmp);
|
||||
if (!fence)
|
||||
if (!fence) {
|
||||
ret = -ENOMEM;
|
||||
goto err_put_timeline;
|
||||
}
|
||||
|
||||
chain = dma_fence_chain_alloc();
|
||||
if (!chain) {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
config DRM_LOGICVC
|
||||
tristate "LogiCVC DRM"
|
||||
depends on DRM
|
||||
depends on OF || COMPILE_TEST
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
help
|
||||
DRM display driver for the logiCVC programmable logic block from Xylon
|
|
@ -0,0 +1,9 @@
|
|||
logicvc-drm-y += \
|
||||
logicvc_crtc.o \
|
||||
logicvc_drm.o \
|
||||
logicvc_interface.o \
|
||||
logicvc_layer.o \
|
||||
logicvc_mode.o \
|
||||
logicvc_of.o
|
||||
|
||||
obj-$(CONFIG_DRM_LOGICVC) += logicvc-drm.o
|
|
@ -0,0 +1,280 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "logicvc_crtc.h"
|
||||
#include "logicvc_drm.h"
|
||||
#include "logicvc_interface.h"
|
||||
#include "logicvc_layer.h"
|
||||
#include "logicvc_regs.h"
|
||||
|
||||
#define logicvc_crtc(c) \
|
||||
container_of(c, struct logicvc_crtc, drm_crtc)
|
||||
|
||||
static enum drm_mode_status
|
||||
logicvc_crtc_mode_valid(struct drm_crtc *drm_crtc,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void logicvc_crtc_atomic_begin(struct drm_crtc *drm_crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
|
||||
struct drm_crtc_state *old_state =
|
||||
drm_atomic_get_old_crtc_state(state, drm_crtc);
|
||||
struct drm_device *drm_dev = drm_crtc->dev;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* We need to grab the pending event here if vblank was already enabled
|
||||
* since we won't get a call to atomic_enable to grab it.
|
||||
*/
|
||||
if (drm_crtc->state->event && old_state->active) {
|
||||
spin_lock_irqsave(&drm_dev->event_lock, flags);
|
||||
WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
|
||||
|
||||
crtc->event = drm_crtc->state->event;
|
||||
drm_crtc->state->event = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&drm_dev->event_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void logicvc_crtc_atomic_enable(struct drm_crtc *drm_crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
|
||||
struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
|
||||
struct drm_crtc_state *old_state =
|
||||
drm_atomic_get_old_crtc_state(state, drm_crtc);
|
||||
struct drm_crtc_state *new_state =
|
||||
drm_atomic_get_new_crtc_state(state, drm_crtc);
|
||||
struct drm_display_mode *mode = &new_state->adjusted_mode;
|
||||
|
||||
struct drm_device *drm_dev = drm_crtc->dev;
|
||||
unsigned int hact, hfp, hsl, hbp;
|
||||
unsigned int vact, vfp, vsl, vbp;
|
||||
unsigned long flags;
|
||||
u32 ctrl;
|
||||
|
||||
/* Timings */
|
||||
|
||||
hact = mode->hdisplay;
|
||||
hfp = mode->hsync_start - mode->hdisplay;
|
||||
hsl = mode->hsync_end - mode->hsync_start;
|
||||
hbp = mode->htotal - mode->hsync_end;
|
||||
|
||||
vact = mode->vdisplay;
|
||||
vfp = mode->vsync_start - mode->vdisplay;
|
||||
vsl = mode->vsync_end - mode->vsync_start;
|
||||
vbp = mode->vtotal - mode->vsync_end;
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_HSYNC_FRONT_PORCH_REG, hfp - 1);
|
||||
regmap_write(logicvc->regmap, LOGICVC_HSYNC_REG, hsl - 1);
|
||||
regmap_write(logicvc->regmap, LOGICVC_HSYNC_BACK_PORCH_REG, hbp - 1);
|
||||
regmap_write(logicvc->regmap, LOGICVC_HRES_REG, hact - 1);
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_VSYNC_FRONT_PORCH_REG, vfp - 1);
|
||||
regmap_write(logicvc->regmap, LOGICVC_VSYNC_REG, vsl - 1);
|
||||
regmap_write(logicvc->regmap, LOGICVC_VSYNC_BACK_PORCH_REG, vbp - 1);
|
||||
regmap_write(logicvc->regmap, LOGICVC_VRES_REG, vact - 1);
|
||||
|
||||
/* Signals */
|
||||
|
||||
ctrl = LOGICVC_CTRL_HSYNC_ENABLE | LOGICVC_CTRL_VSYNC_ENABLE |
|
||||
LOGICVC_CTRL_DE_ENABLE;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
ctrl |= LOGICVC_CTRL_HSYNC_INVERT;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
ctrl |= LOGICVC_CTRL_VSYNC_INVERT;
|
||||
|
||||
if (logicvc->interface) {
|
||||
struct drm_connector *connector =
|
||||
&logicvc->interface->drm_connector;
|
||||
struct drm_display_info *display_info =
|
||||
&connector->display_info;
|
||||
|
||||
if (display_info->bus_flags & DRM_BUS_FLAG_DE_LOW)
|
||||
ctrl |= LOGICVC_CTRL_DE_INVERT;
|
||||
|
||||
if (display_info->bus_flags &
|
||||
DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
|
||||
ctrl |= LOGICVC_CTRL_CLOCK_INVERT;
|
||||
}
|
||||
|
||||
regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
|
||||
LOGICVC_CTRL_HSYNC_ENABLE |
|
||||
LOGICVC_CTRL_HSYNC_INVERT |
|
||||
LOGICVC_CTRL_VSYNC_ENABLE |
|
||||
LOGICVC_CTRL_VSYNC_INVERT |
|
||||
LOGICVC_CTRL_DE_ENABLE |
|
||||
LOGICVC_CTRL_DE_INVERT |
|
||||
LOGICVC_CTRL_PIXEL_INVERT |
|
||||
LOGICVC_CTRL_CLOCK_INVERT, ctrl);
|
||||
|
||||
/* Generate internal state reset. */
|
||||
regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
|
||||
|
||||
drm_crtc_vblank_on(drm_crtc);
|
||||
|
||||
/* Register our event after vblank is enabled. */
|
||||
if (drm_crtc->state->event && !old_state->active) {
|
||||
spin_lock_irqsave(&drm_dev->event_lock, flags);
|
||||
WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
|
||||
|
||||
crtc->event = drm_crtc->state->event;
|
||||
drm_crtc->state->event = NULL;
|
||||
spin_unlock_irqrestore(&drm_dev->event_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void logicvc_crtc_atomic_disable(struct drm_crtc *drm_crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
|
||||
struct drm_device *drm_dev = drm_crtc->dev;
|
||||
|
||||
drm_crtc_vblank_off(drm_crtc);
|
||||
|
||||
/* Disable and clear CRTC bits. */
|
||||
regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
|
||||
LOGICVC_CTRL_HSYNC_ENABLE |
|
||||
LOGICVC_CTRL_HSYNC_INVERT |
|
||||
LOGICVC_CTRL_VSYNC_ENABLE |
|
||||
LOGICVC_CTRL_VSYNC_INVERT |
|
||||
LOGICVC_CTRL_DE_ENABLE |
|
||||
LOGICVC_CTRL_DE_INVERT |
|
||||
LOGICVC_CTRL_PIXEL_INVERT |
|
||||
LOGICVC_CTRL_CLOCK_INVERT, 0);
|
||||
|
||||
/* Generate internal state reset. */
|
||||
regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
|
||||
|
||||
/* Consume any leftover event since vblank is now disabled. */
|
||||
if (drm_crtc->state->event && !drm_crtc->state->active) {
|
||||
spin_lock_irq(&drm_dev->event_lock);
|
||||
|
||||
drm_crtc_send_vblank_event(drm_crtc, drm_crtc->state->event);
|
||||
drm_crtc->state->event = NULL;
|
||||
spin_unlock_irq(&drm_dev->event_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs logicvc_crtc_helper_funcs = {
|
||||
.mode_valid = logicvc_crtc_mode_valid,
|
||||
.atomic_begin = logicvc_crtc_atomic_begin,
|
||||
.atomic_enable = logicvc_crtc_atomic_enable,
|
||||
.atomic_disable = logicvc_crtc_atomic_disable,
|
||||
};
|
||||
|
||||
static int logicvc_crtc_enable_vblank(struct drm_crtc *drm_crtc)
|
||||
{
|
||||
struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
|
||||
|
||||
/* Clear any pending V_SYNC interrupt. */
|
||||
regmap_write_bits(logicvc->regmap, LOGICVC_INT_STAT_REG,
|
||||
LOGICVC_INT_STAT_V_SYNC, LOGICVC_INT_STAT_V_SYNC);
|
||||
|
||||
/* Unmask V_SYNC interrupt. */
|
||||
regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
|
||||
LOGICVC_INT_MASK_V_SYNC, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void logicvc_crtc_disable_vblank(struct drm_crtc *drm_crtc)
|
||||
{
|
||||
struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
|
||||
|
||||
/* Mask V_SYNC interrupt. */
|
||||
regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
|
||||
LOGICVC_INT_MASK_V_SYNC, LOGICVC_INT_MASK_V_SYNC);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs logicvc_crtc_funcs = {
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.enable_vblank = logicvc_crtc_enable_vblank,
|
||||
.disable_vblank = logicvc_crtc_disable_vblank,
|
||||
};
|
||||
|
||||
void logicvc_crtc_vblank_handler(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct logicvc_crtc *crtc = logicvc->crtc;
|
||||
unsigned long flags;
|
||||
|
||||
if (!crtc)
|
||||
return;
|
||||
|
||||
drm_crtc_handle_vblank(&crtc->drm_crtc);
|
||||
|
||||
if (crtc->event) {
|
||||
spin_lock_irqsave(&drm_dev->event_lock, flags);
|
||||
drm_crtc_send_vblank_event(&crtc->drm_crtc, crtc->event);
|
||||
drm_crtc_vblank_put(&crtc->drm_crtc);
|
||||
crtc->event = NULL;
|
||||
spin_unlock_irqrestore(&drm_dev->event_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
int logicvc_crtc_init(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct device *dev = drm_dev->dev;
|
||||
struct device_node *of_node = dev->of_node;
|
||||
struct logicvc_crtc *crtc;
|
||||
struct logicvc_layer *layer_primary;
|
||||
int ret;
|
||||
|
||||
crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
|
||||
if (!crtc)
|
||||
return -ENOMEM;
|
||||
|
||||
layer_primary = logicvc_layer_get_primary(logicvc);
|
||||
if (!layer_primary) {
|
||||
drm_err(drm_dev, "Failed to get primary layer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = drm_crtc_init_with_planes(drm_dev, &crtc->drm_crtc,
|
||||
&layer_primary->drm_plane, NULL,
|
||||
&logicvc_crtc_funcs, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to initialize CRTC\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_crtc_helper_add(&crtc->drm_crtc, &logicvc_crtc_helper_funcs);
|
||||
|
||||
crtc->drm_crtc.port = of_graph_get_port_by_id(of_node, 1);
|
||||
|
||||
logicvc->crtc = crtc;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef _LOGICVC_CRTC_H_
|
||||
#define _LOGICVC_CRTC_H_
|
||||
|
||||
struct drm_pending_vblank_event;
|
||||
struct logicvc_drm;
|
||||
|
||||
struct logicvc_crtc {
|
||||
struct drm_crtc drm_crtc;
|
||||
struct drm_pending_vblank_event *event;
|
||||
};
|
||||
|
||||
void logicvc_crtc_vblank_handler(struct logicvc_drm *logicvc);
|
||||
int logicvc_crtc_init(struct logicvc_drm *logicvc);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,497 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "logicvc_crtc.h"
|
||||
#include "logicvc_drm.h"
|
||||
#include "logicvc_interface.h"
|
||||
#include "logicvc_mode.h"
|
||||
#include "logicvc_layer.h"
|
||||
#include "logicvc_of.h"
|
||||
#include "logicvc_regs.h"
|
||||
|
||||
DEFINE_DRM_GEM_CMA_FOPS(logicvc_drm_fops);
|
||||
|
||||
static int logicvc_drm_gem_cma_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *drm_dev,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
struct logicvc_drm *logicvc = logicvc_drm(drm_dev);
|
||||
|
||||
/* Stride is always fixed to its configuration value. */
|
||||
args->pitch = logicvc->config.row_stride * DIV_ROUND_UP(args->bpp, 8);
|
||||
|
||||
return drm_gem_cma_dumb_create_internal(file_priv, drm_dev, args);
|
||||
}
|
||||
|
||||
static struct drm_driver logicvc_drm_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET |
|
||||
DRIVER_ATOMIC,
|
||||
|
||||
.fops = &logicvc_drm_fops,
|
||||
.name = "logicvc-drm",
|
||||
.desc = "Xylon LogiCVC DRM driver",
|
||||
.date = "20200403",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
|
||||
DRM_GEM_CMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(logicvc_drm_gem_cma_dumb_create),
|
||||
};
|
||||
|
||||
static struct regmap_config logicvc_drm_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.name = "logicvc-drm",
|
||||
};
|
||||
|
||||
static irqreturn_t logicvc_drm_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct logicvc_drm *logicvc = data;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 stat = 0;
|
||||
|
||||
/* Get pending interrupt sources. */
|
||||
regmap_read(logicvc->regmap, LOGICVC_INT_STAT_REG, &stat);
|
||||
|
||||
/* Clear all pending interrupt sources. */
|
||||
regmap_write(logicvc->regmap, LOGICVC_INT_STAT_REG, stat);
|
||||
|
||||
if (stat & LOGICVC_INT_STAT_V_SYNC) {
|
||||
logicvc_crtc_vblank_handler(logicvc);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int logicvc_drm_config_parse(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct device *dev = drm_dev->dev;
|
||||
struct device_node *of_node = dev->of_node;
|
||||
struct logicvc_drm_config *config = &logicvc->config;
|
||||
struct device_node *layers_node;
|
||||
int ret;
|
||||
|
||||
logicvc_of_property_parse_bool(of_node, LOGICVC_OF_PROPERTY_DITHERING,
|
||||
&config->dithering);
|
||||
logicvc_of_property_parse_bool(of_node,
|
||||
LOGICVC_OF_PROPERTY_BACKGROUND_LAYER,
|
||||
&config->background_layer);
|
||||
logicvc_of_property_parse_bool(of_node,
|
||||
LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE,
|
||||
&config->layers_configurable);
|
||||
|
||||
ret = logicvc_of_property_parse_u32(of_node,
|
||||
LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE,
|
||||
&config->display_interface);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = logicvc_of_property_parse_u32(of_node,
|
||||
LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE,
|
||||
&config->display_colorspace);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = logicvc_of_property_parse_u32(of_node,
|
||||
LOGICVC_OF_PROPERTY_DISPLAY_DEPTH,
|
||||
&config->display_depth);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = logicvc_of_property_parse_u32(of_node,
|
||||
LOGICVC_OF_PROPERTY_ROW_STRIDE,
|
||||
&config->row_stride);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
layers_node = of_get_child_by_name(of_node, "layers");
|
||||
if (!layers_node) {
|
||||
drm_err(drm_dev, "Missing non-optional layers node\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config->layers_count = of_get_child_count(layers_node);
|
||||
if (!config->layers_count) {
|
||||
drm_err(drm_dev,
|
||||
"Missing a non-optional layers children node\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int logicvc_clocks_prepare(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct device *dev = drm_dev->dev;
|
||||
|
||||
struct {
|
||||
struct clk **clk;
|
||||
char *name;
|
||||
bool optional;
|
||||
} clocks_map[] = {
|
||||
{
|
||||
.clk = &logicvc->vclk,
|
||||
.name = "vclk",
|
||||
.optional = false,
|
||||
},
|
||||
{
|
||||
.clk = &logicvc->vclk2,
|
||||
.name = "vclk2",
|
||||
.optional = true,
|
||||
},
|
||||
{
|
||||
.clk = &logicvc->lvdsclk,
|
||||
.name = "lvdsclk",
|
||||
.optional = true,
|
||||
},
|
||||
{
|
||||
.clk = &logicvc->lvdsclkn,
|
||||
.name = "lvdsclkn",
|
||||
.optional = true,
|
||||
},
|
||||
};
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clocks_map); i++) {
|
||||
struct clk *clk;
|
||||
|
||||
clk = devm_clk_get(dev, clocks_map[i].name);
|
||||
if (IS_ERR(clk)) {
|
||||
if (PTR_ERR(clk) == -ENOENT && clocks_map[i].optional)
|
||||
continue;
|
||||
|
||||
drm_err(drm_dev, "Missing non-optional clock %s\n",
|
||||
clocks_map[i].name);
|
||||
|
||||
ret = PTR_ERR(clk);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
drm_err(drm_dev,
|
||||
"Failed to prepare and enable clock %s\n",
|
||||
clocks_map[i].name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
*clocks_map[i].clk = clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
for (i = 0; i < ARRAY_SIZE(clocks_map); i++) {
|
||||
if (!*clocks_map[i].clk)
|
||||
continue;
|
||||
|
||||
clk_disable_unprepare(*clocks_map[i].clk);
|
||||
*clocks_map[i].clk = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int logicvc_clocks_unprepare(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct clk **clocks[] = {
|
||||
&logicvc->vclk,
|
||||
&logicvc->vclk2,
|
||||
&logicvc->lvdsclk,
|
||||
&logicvc->lvdsclkn,
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clocks); i++) {
|
||||
if (!*clocks[i])
|
||||
continue;
|
||||
|
||||
clk_disable_unprepare(*clocks[i]);
|
||||
*clocks[i] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct logicvc_drm_caps logicvc_drm_caps[] = {
|
||||
{
|
||||
.major = 3,
|
||||
.layer_address = false,
|
||||
},
|
||||
{
|
||||
.major = 4,
|
||||
.layer_address = true,
|
||||
},
|
||||
{
|
||||
.major = 5,
|
||||
.layer_address = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct logicvc_drm_caps *
|
||||
logicvc_drm_caps_match(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
const struct logicvc_drm_caps *caps = NULL;
|
||||
unsigned int major, minor;
|
||||
char level;
|
||||
unsigned int i;
|
||||
u32 version;
|
||||
|
||||
regmap_read(logicvc->regmap, LOGICVC_IP_VERSION_REG, &version);
|
||||
|
||||
major = FIELD_GET(LOGICVC_IP_VERSION_MAJOR_MASK, version);
|
||||
minor = FIELD_GET(LOGICVC_IP_VERSION_MINOR_MASK, version);
|
||||
level = FIELD_GET(LOGICVC_IP_VERSION_LEVEL_MASK, version) + 'a';
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(logicvc_drm_caps); i++) {
|
||||
if (logicvc_drm_caps[i].major &&
|
||||
logicvc_drm_caps[i].major != major)
|
||||
continue;
|
||||
|
||||
if (logicvc_drm_caps[i].minor &&
|
||||
logicvc_drm_caps[i].minor != minor)
|
||||
continue;
|
||||
|
||||
if (logicvc_drm_caps[i].level &&
|
||||
logicvc_drm_caps[i].level != level)
|
||||
continue;
|
||||
|
||||
caps = &logicvc_drm_caps[i];
|
||||
}
|
||||
|
||||
drm_info(drm_dev, "LogiCVC version %d.%02d.%c\n", major, minor, level);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static int logicvc_drm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *of_node = pdev->dev.of_node;
|
||||
struct device_node *reserved_mem_node;
|
||||
struct reserved_mem *reserved_mem = NULL;
|
||||
const struct logicvc_drm_caps *caps;
|
||||
struct logicvc_drm *logicvc;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct regmap *regmap;
|
||||
struct resource res;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
ret = of_reserved_mem_device_init(dev);
|
||||
if (ret && ret != -ENODEV) {
|
||||
dev_err(dev, "Failed to init memory region\n");
|
||||
goto error_early;
|
||||
}
|
||||
|
||||
reserved_mem_node = of_parse_phandle(of_node, "memory-region", 0);
|
||||
if (reserved_mem_node) {
|
||||
reserved_mem = of_reserved_mem_lookup(reserved_mem_node);
|
||||
of_node_put(reserved_mem_node);
|
||||
}
|
||||
|
||||
/* Get regmap from parent if available. */
|
||||
if (of_node->parent)
|
||||
regmap = syscon_node_to_regmap(of_node->parent);
|
||||
|
||||
/* Register our own regmap otherwise. */
|
||||
if (IS_ERR_OR_NULL(regmap)) {
|
||||
ret = of_address_to_resource(of_node, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get resource from address\n");
|
||||
goto error_reserved_mem;
|
||||
}
|
||||
|
||||
base = devm_ioremap_resource(dev, &res);
|
||||
if (IS_ERR(base)) {
|
||||
dev_err(dev, "Failed to map I/O base\n");
|
||||
ret = PTR_ERR(base);
|
||||
goto error_reserved_mem;
|
||||
}
|
||||
|
||||
logicvc_drm_regmap_config.max_register = resource_size(&res) -
|
||||
4;
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base,
|
||||
&logicvc_drm_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Failed to create regmap for I/O\n");
|
||||
ret = PTR_ERR(regmap);
|
||||
goto error_reserved_mem;
|
||||
}
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "Failed to get IRQ\n");
|
||||
ret = -ENODEV;
|
||||
goto error_reserved_mem;
|
||||
}
|
||||
|
||||
logicvc = devm_drm_dev_alloc(dev, &logicvc_drm_driver,
|
||||
struct logicvc_drm, drm_dev);
|
||||
if (IS_ERR(logicvc)) {
|
||||
ret = PTR_ERR(logicvc);
|
||||
goto error_reserved_mem;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, logicvc);
|
||||
drm_dev = &logicvc->drm_dev;
|
||||
|
||||
logicvc->regmap = regmap;
|
||||
INIT_LIST_HEAD(&logicvc->layers_list);
|
||||
|
||||
caps = logicvc_drm_caps_match(logicvc);
|
||||
if (!caps) {
|
||||
ret = -EINVAL;
|
||||
goto error_reserved_mem;
|
||||
}
|
||||
|
||||
logicvc->caps = caps;
|
||||
|
||||
if (reserved_mem)
|
||||
logicvc->reserved_mem_base = reserved_mem->base;
|
||||
|
||||
ret = logicvc_clocks_prepare(logicvc);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to prepare clocks\n");
|
||||
goto error_reserved_mem;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, irq, logicvc_drm_irq_handler, 0,
|
||||
dev_name(dev), logicvc);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to request IRQ\n");
|
||||
goto error_clocks;
|
||||
}
|
||||
|
||||
ret = logicvc_drm_config_parse(logicvc);
|
||||
if (ret && ret != -ENODEV) {
|
||||
drm_err(drm_dev, "Failed to parse config\n");
|
||||
goto error_clocks;
|
||||
}
|
||||
|
||||
ret = drmm_mode_config_init(drm_dev);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to init mode config\n");
|
||||
goto error_clocks;
|
||||
}
|
||||
|
||||
ret = logicvc_layers_init(logicvc);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to initialize layers\n");
|
||||
goto error_clocks;
|
||||
}
|
||||
|
||||
ret = logicvc_crtc_init(logicvc);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to initialize CRTC\n");
|
||||
goto error_clocks;
|
||||
}
|
||||
|
||||
logicvc_layers_attach_crtc(logicvc);
|
||||
|
||||
ret = logicvc_interface_init(logicvc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
drm_err(drm_dev, "Failed to initialize interface\n");
|
||||
|
||||
goto error_clocks;
|
||||
}
|
||||
|
||||
logicvc_interface_attach_crtc(logicvc);
|
||||
|
||||
ret = logicvc_mode_init(logicvc);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to initialize KMS\n");
|
||||
goto error_clocks;
|
||||
}
|
||||
|
||||
ret = drm_dev_register(drm_dev, 0);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to register DRM device\n");
|
||||
goto error_mode;
|
||||
}
|
||||
|
||||
drm_fbdev_generic_setup(drm_dev, drm_dev->mode_config.preferred_depth);
|
||||
|
||||
return 0;
|
||||
|
||||
error_mode:
|
||||
logicvc_mode_fini(logicvc);
|
||||
|
||||
error_clocks:
|
||||
logicvc_clocks_unprepare(logicvc);
|
||||
|
||||
error_reserved_mem:
|
||||
of_reserved_mem_device_release(dev);
|
||||
|
||||
error_early:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int logicvc_drm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct logicvc_drm *logicvc = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
|
||||
drm_dev_unregister(drm_dev);
|
||||
drm_atomic_helper_shutdown(drm_dev);
|
||||
|
||||
logicvc_mode_fini(logicvc);
|
||||
|
||||
logicvc_clocks_unprepare(logicvc);
|
||||
|
||||
of_reserved_mem_device_release(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id logicvc_drm_of_table[] = {
|
||||
{ .compatible = "xylon,logicvc-3.02.a-display" },
|
||||
{ .compatible = "xylon,logicvc-4.01.a-display" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, logicvc_drm_of_table);
|
||||
|
||||
static struct platform_driver logicvc_drm_platform_driver = {
|
||||
.probe = logicvc_drm_probe,
|
||||
.remove = logicvc_drm_remove,
|
||||
.driver = {
|
||||
.name = "logicvc-drm",
|
||||
.of_match_table = logicvc_drm_of_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(logicvc_drm_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Xylon LogiCVC DRM driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,67 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef _LOGICVC_DRM_H_
|
||||
#define _LOGICVC_DRM_H_
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
#include <drm/drm_device.h>
|
||||
|
||||
#define LOGICVC_DISPLAY_INTERFACE_RGB 0
|
||||
#define LOGICVC_DISPLAY_INTERFACE_ITU656 1
|
||||
#define LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS 2
|
||||
#define LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA 3
|
||||
#define LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS 4
|
||||
#define LOGICVC_DISPLAY_INTERFACE_DVI 5
|
||||
|
||||
#define LOGICVC_DISPLAY_COLORSPACE_RGB 0
|
||||
#define LOGICVC_DISPLAY_COLORSPACE_YUV422 1
|
||||
#define LOGICVC_DISPLAY_COLORSPACE_YUV444 2
|
||||
|
||||
#define logicvc_drm(d) \
|
||||
container_of(d, struct logicvc_drm, drm_dev)
|
||||
|
||||
struct logicvc_crtc;
|
||||
struct logicvc_interface;
|
||||
|
||||
struct logicvc_drm_config {
|
||||
u32 display_interface;
|
||||
u32 display_colorspace;
|
||||
u32 display_depth;
|
||||
u32 row_stride;
|
||||
bool dithering;
|
||||
bool background_layer;
|
||||
bool layers_configurable;
|
||||
u32 layers_count;
|
||||
};
|
||||
|
||||
struct logicvc_drm_caps {
|
||||
unsigned int major;
|
||||
unsigned int minor;
|
||||
char level;
|
||||
bool layer_address;
|
||||
};
|
||||
|
||||
struct logicvc_drm {
|
||||
const struct logicvc_drm_caps *caps;
|
||||
struct logicvc_drm_config config;
|
||||
|
||||
struct drm_device drm_dev;
|
||||
phys_addr_t reserved_mem_base;
|
||||
struct regmap *regmap;
|
||||
|
||||
struct clk *vclk;
|
||||
struct clk *vclk2;
|
||||
struct clk *lvdsclk;
|
||||
struct clk *lvdsclkn;
|
||||
|
||||
struct list_head layers_list;
|
||||
struct logicvc_crtc *crtc;
|
||||
struct logicvc_interface *interface;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,214 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#include "logicvc_crtc.h"
|
||||
#include "logicvc_drm.h"
|
||||
#include "logicvc_interface.h"
|
||||
#include "logicvc_regs.h"
|
||||
|
||||
#define logicvc_interface_from_drm_encoder(c) \
|
||||
container_of(c, struct logicvc_interface, drm_encoder)
|
||||
#define logicvc_interface_from_drm_connector(c) \
|
||||
container_of(c, struct logicvc_interface, drm_connector)
|
||||
|
||||
static void logicvc_encoder_enable(struct drm_encoder *drm_encoder)
|
||||
{
|
||||
struct logicvc_drm *logicvc = logicvc_drm(drm_encoder->dev);
|
||||
struct logicvc_interface *interface =
|
||||
logicvc_interface_from_drm_encoder(drm_encoder);
|
||||
|
||||
regmap_update_bits(logicvc->regmap, LOGICVC_POWER_CTRL_REG,
|
||||
LOGICVC_POWER_CTRL_VIDEO_ENABLE,
|
||||
LOGICVC_POWER_CTRL_VIDEO_ENABLE);
|
||||
|
||||
if (interface->drm_panel) {
|
||||
drm_panel_prepare(interface->drm_panel);
|
||||
drm_panel_enable(interface->drm_panel);
|
||||
}
|
||||
}
|
||||
|
||||
static void logicvc_encoder_disable(struct drm_encoder *drm_encoder)
|
||||
{
|
||||
struct logicvc_interface *interface =
|
||||
logicvc_interface_from_drm_encoder(drm_encoder);
|
||||
|
||||
if (interface->drm_panel) {
|
||||
drm_panel_disable(interface->drm_panel);
|
||||
drm_panel_unprepare(interface->drm_panel);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs logicvc_encoder_helper_funcs = {
|
||||
.enable = logicvc_encoder_enable,
|
||||
.disable = logicvc_encoder_disable,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_funcs logicvc_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static int logicvc_connector_get_modes(struct drm_connector *drm_connector)
|
||||
{
|
||||
struct logicvc_interface *interface =
|
||||
logicvc_interface_from_drm_connector(drm_connector);
|
||||
|
||||
if (interface->drm_panel)
|
||||
return drm_panel_get_modes(interface->drm_panel, drm_connector);
|
||||
|
||||
WARN_ONCE(1, "Retrieving modes from a native connector is not implemented.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs logicvc_connector_helper_funcs = {
|
||||
.get_modes = logicvc_connector_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs logicvc_connector_funcs = {
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int logicvc_interface_encoder_type(struct logicvc_drm *logicvc)
|
||||
{
|
||||
switch (logicvc->config.display_interface) {
|
||||
case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS:
|
||||
case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA:
|
||||
case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS:
|
||||
return DRM_MODE_ENCODER_LVDS;
|
||||
case LOGICVC_DISPLAY_INTERFACE_DVI:
|
||||
return DRM_MODE_ENCODER_TMDS;
|
||||
case LOGICVC_DISPLAY_INTERFACE_RGB:
|
||||
return DRM_MODE_ENCODER_DPI;
|
||||
default:
|
||||
return DRM_MODE_ENCODER_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static int logicvc_interface_connector_type(struct logicvc_drm *logicvc)
|
||||
{
|
||||
switch (logicvc->config.display_interface) {
|
||||
case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS:
|
||||
case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA:
|
||||
case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS:
|
||||
return DRM_MODE_CONNECTOR_LVDS;
|
||||
case LOGICVC_DISPLAY_INTERFACE_DVI:
|
||||
return DRM_MODE_CONNECTOR_DVID;
|
||||
case LOGICVC_DISPLAY_INTERFACE_RGB:
|
||||
return DRM_MODE_CONNECTOR_DPI;
|
||||
default:
|
||||
return DRM_MODE_CONNECTOR_Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
static bool logicvc_interface_native_connector(struct logicvc_drm *logicvc)
|
||||
{
|
||||
switch (logicvc->config.display_interface) {
|
||||
case LOGICVC_DISPLAY_INTERFACE_DVI:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void logicvc_interface_attach_crtc(struct logicvc_drm *logicvc)
|
||||
{
|
||||
uint32_t possible_crtcs = drm_crtc_mask(&logicvc->crtc->drm_crtc);
|
||||
|
||||
logicvc->interface->drm_encoder.possible_crtcs = possible_crtcs;
|
||||
}
|
||||
|
||||
int logicvc_interface_init(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct logicvc_interface *interface;
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct device *dev = drm_dev->dev;
|
||||
struct device_node *of_node = dev->of_node;
|
||||
int encoder_type = logicvc_interface_encoder_type(logicvc);
|
||||
int connector_type = logicvc_interface_connector_type(logicvc);
|
||||
bool native_connector = logicvc_interface_native_connector(logicvc);
|
||||
int ret;
|
||||
|
||||
interface = devm_kzalloc(dev, sizeof(*interface), GFP_KERNEL);
|
||||
if (!interface) {
|
||||
ret = -ENOMEM;
|
||||
goto error_early;
|
||||
}
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(of_node, 0, 0, &interface->drm_panel,
|
||||
&interface->drm_bridge);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto error_early;
|
||||
|
||||
ret = drm_encoder_init(drm_dev, &interface->drm_encoder,
|
||||
&logicvc_encoder_funcs, encoder_type, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to initialize encoder\n");
|
||||
goto error_early;
|
||||
}
|
||||
|
||||
drm_encoder_helper_add(&interface->drm_encoder,
|
||||
&logicvc_encoder_helper_funcs);
|
||||
|
||||
if (native_connector || interface->drm_panel) {
|
||||
ret = drm_connector_init(drm_dev, &interface->drm_connector,
|
||||
&logicvc_connector_funcs,
|
||||
connector_type);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to initialize connector\n");
|
||||
goto error_encoder;
|
||||
}
|
||||
|
||||
drm_connector_helper_add(&interface->drm_connector,
|
||||
&logicvc_connector_helper_funcs);
|
||||
|
||||
ret = drm_connector_attach_encoder(&interface->drm_connector,
|
||||
&interface->drm_encoder);
|
||||
if (ret) {
|
||||
drm_err(drm_dev,
|
||||
"Failed to attach connector to encoder\n");
|
||||
goto error_encoder;
|
||||
}
|
||||
}
|
||||
|
||||
if (interface->drm_bridge) {
|
||||
ret = drm_bridge_attach(&interface->drm_encoder,
|
||||
interface->drm_bridge, NULL, 0);
|
||||
if (ret) {
|
||||
drm_err(drm_dev,
|
||||
"Failed to attach bridge to encoder\n");
|
||||
goto error_encoder;
|
||||
}
|
||||
}
|
||||
|
||||
logicvc->interface = interface;
|
||||
|
||||
return 0;
|
||||
|
||||
error_encoder:
|
||||
drm_encoder_cleanup(&interface->drm_encoder);
|
||||
|
||||
error_early:
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef _LOGICVC_INTERFACE_H_
|
||||
#define _LOGICVC_INTERFACE_H_
|
||||
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
struct logicvc_drm;
|
||||
|
||||
struct logicvc_interface {
|
||||
struct drm_encoder drm_encoder;
|
||||
struct drm_connector drm_connector;
|
||||
|
||||
struct drm_panel *drm_panel;
|
||||
struct drm_bridge *drm_bridge;
|
||||
};
|
||||
|
||||
void logicvc_interface_attach_crtc(struct logicvc_drm *logicvc);
|
||||
int logicvc_interface_init(struct logicvc_drm *logicvc);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,628 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "logicvc_crtc.h"
|
||||
#include "logicvc_drm.h"
|
||||
#include "logicvc_layer.h"
|
||||
#include "logicvc_of.h"
|
||||
#include "logicvc_regs.h"
|
||||
|
||||
#define logicvc_layer(p) \
|
||||
container_of(p, struct logicvc_layer, drm_plane)
|
||||
|
||||
static uint32_t logicvc_layer_formats_rgb16[] = {
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_BGR565,
|
||||
DRM_FORMAT_INVALID,
|
||||
};
|
||||
|
||||
static uint32_t logicvc_layer_formats_rgb24[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_INVALID,
|
||||
};
|
||||
|
||||
/*
|
||||
* What we call depth in this driver only counts color components, not alpha.
|
||||
* This allows us to stay compatible with the LogiCVC bistream definitions.
|
||||
*/
|
||||
static uint32_t logicvc_layer_formats_rgb24_alpha[] = {
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_INVALID,
|
||||
};
|
||||
|
||||
static struct logicvc_layer_formats logicvc_layer_formats[] = {
|
||||
{
|
||||
.colorspace = LOGICVC_LAYER_COLORSPACE_RGB,
|
||||
.depth = 16,
|
||||
.formats = logicvc_layer_formats_rgb16,
|
||||
},
|
||||
{
|
||||
.colorspace = LOGICVC_LAYER_COLORSPACE_RGB,
|
||||
.depth = 24,
|
||||
.formats = logicvc_layer_formats_rgb24,
|
||||
},
|
||||
{
|
||||
.colorspace = LOGICVC_LAYER_COLORSPACE_RGB,
|
||||
.depth = 24,
|
||||
.alpha = true,
|
||||
.formats = logicvc_layer_formats_rgb24_alpha,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static bool logicvc_layer_format_inverted(uint32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_BGR565:
|
||||
case DRM_FORMAT_BGR888:
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int logicvc_plane_atomic_check(struct drm_plane *drm_plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *drm_dev = drm_plane->dev;
|
||||
struct logicvc_layer *layer = logicvc_layer(drm_plane);
|
||||
struct logicvc_drm *logicvc = logicvc_drm(drm_dev);
|
||||
struct drm_plane_state *new_state =
|
||||
drm_atomic_get_new_plane_state(state, drm_plane);
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int min_scale, max_scale;
|
||||
bool can_position;
|
||||
int ret;
|
||||
|
||||
if (!new_state->crtc)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(new_state->state,
|
||||
new_state->crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
if (new_state->crtc_x < 0 || new_state->crtc_y < 0) {
|
||||
drm_err(drm_dev,
|
||||
"Negative on-CRTC positions are not supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!logicvc->caps->layer_address) {
|
||||
ret = logicvc_layer_buffer_find_setup(logicvc, layer, new_state,
|
||||
NULL);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "No viable setup for buffer found.\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
min_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
max_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
|
||||
can_position = (drm_plane->type == DRM_PLANE_TYPE_OVERLAY &&
|
||||
layer->index != (logicvc->config.layers_count - 1) &&
|
||||
logicvc->config.layers_configurable);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(new_state, crtc_state,
|
||||
min_scale, max_scale,
|
||||
can_position, true);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Invalid plane state\n\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void logicvc_plane_atomic_update(struct drm_plane *drm_plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct logicvc_layer *layer = logicvc_layer(drm_plane);
|
||||
struct logicvc_drm *logicvc = logicvc_drm(drm_plane->dev);
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct drm_plane_state *new_state =
|
||||
drm_atomic_get_new_plane_state(state, drm_plane);
|
||||
struct drm_crtc *drm_crtc = &logicvc->crtc->drm_crtc;
|
||||
struct drm_display_mode *mode = &drm_crtc->state->adjusted_mode;
|
||||
struct drm_framebuffer *fb = new_state->fb;
|
||||
struct logicvc_layer_buffer_setup setup = {};
|
||||
u32 index = layer->index;
|
||||
u32 reg;
|
||||
|
||||
/* Layer dimensions */
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_WIDTH_REG(index),
|
||||
new_state->crtc_w - 1);
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_HEIGHT_REG(index),
|
||||
new_state->crtc_h - 1);
|
||||
|
||||
if (logicvc->caps->layer_address) {
|
||||
phys_addr_t fb_addr = drm_fb_cma_get_gem_addr(fb, new_state, 0);
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_ADDRESS_REG(index),
|
||||
fb_addr);
|
||||
} else {
|
||||
/* Rely on offsets to configure the address. */
|
||||
|
||||
logicvc_layer_buffer_find_setup(logicvc, layer, new_state,
|
||||
&setup);
|
||||
|
||||
/* Layer memory offsets */
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_BUFFER_SEL_REG,
|
||||
LOGICVC_BUFFER_SEL_VALUE(index, setup.buffer_sel));
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_HOFFSET_REG(index),
|
||||
setup.hoffset);
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_VOFFSET_REG(index),
|
||||
setup.voffset);
|
||||
}
|
||||
|
||||
/* Layer position */
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_HPOSITION_REG(index),
|
||||
mode->hdisplay - 1 - new_state->crtc_x);
|
||||
|
||||
/* Vertical position must be set last to sync layer register changes. */
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_VPOSITION_REG(index),
|
||||
mode->vdisplay - 1 - new_state->crtc_y);
|
||||
|
||||
/* Layer alpha */
|
||||
|
||||
if (layer->config.alpha_mode == LOGICVC_LAYER_ALPHA_LAYER) {
|
||||
u32 alpha_bits;
|
||||
u32 alpha_max;
|
||||
u32 alpha;
|
||||
|
||||
switch (layer->config.depth) {
|
||||
case 8:
|
||||
alpha_bits = 3;
|
||||
break;
|
||||
case 16:
|
||||
if (layer->config.colorspace ==
|
||||
LOGICVC_LAYER_COLORSPACE_YUV)
|
||||
alpha_bits = 8;
|
||||
else
|
||||
alpha_bits = 6;
|
||||
break;
|
||||
default:
|
||||
alpha_bits = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
alpha_max = BIT(alpha_bits) - 1;
|
||||
alpha = new_state->alpha * alpha_max / DRM_BLEND_ALPHA_OPAQUE;
|
||||
|
||||
drm_dbg_kms(drm_dev, "Setting layer %d alpha to %d/%d\n", index,
|
||||
alpha, alpha_max);
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_ALPHA_REG(index),
|
||||
alpha);
|
||||
}
|
||||
|
||||
/* Layer control */
|
||||
|
||||
reg = LOGICVC_LAYER_CTRL_ENABLE;
|
||||
|
||||
if (logicvc_layer_format_inverted(fb->format->format))
|
||||
reg |= LOGICVC_LAYER_CTRL_PIXEL_FORMAT_INVERT;
|
||||
|
||||
reg |= LOGICVC_LAYER_CTRL_COLOR_KEY_DISABLE;
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_CTRL_REG(index), reg);
|
||||
}
|
||||
|
||||
static void logicvc_plane_atomic_disable(struct drm_plane *drm_plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct logicvc_layer *layer = logicvc_layer(drm_plane);
|
||||
struct logicvc_drm *logicvc = logicvc_drm(drm_plane->dev);
|
||||
u32 index = layer->index;
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_LAYER_CTRL_REG(index), 0);
|
||||
}
|
||||
|
||||
static struct drm_plane_helper_funcs logicvc_plane_helper_funcs = {
|
||||
.atomic_check = logicvc_plane_atomic_check,
|
||||
.atomic_update = logicvc_plane_atomic_update,
|
||||
.atomic_disable = logicvc_plane_atomic_disable,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs logicvc_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = drm_plane_cleanup,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
};
|
||||
|
||||
int logicvc_layer_buffer_find_setup(struct logicvc_drm *logicvc,
|
||||
struct logicvc_layer *layer,
|
||||
struct drm_plane_state *state,
|
||||
struct logicvc_layer_buffer_setup *setup)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
/* All the supported formats have a single data plane. */
|
||||
u32 layer_bytespp = fb->format->cpp[0];
|
||||
u32 layer_stride = layer_bytespp * logicvc->config.row_stride;
|
||||
u32 base_offset = layer->config.base_offset * layer_stride;
|
||||
u32 buffer_offset = layer->config.buffer_offset * layer_stride;
|
||||
u8 buffer_sel = 0;
|
||||
u16 voffset = 0;
|
||||
u16 hoffset = 0;
|
||||
phys_addr_t fb_addr;
|
||||
u32 fb_offset;
|
||||
u32 gap;
|
||||
|
||||
if (!logicvc->reserved_mem_base) {
|
||||
drm_err(drm_dev, "No reserved memory base was registered!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fb_addr = drm_fb_cma_get_gem_addr(fb, state, 0);
|
||||
if (fb_addr < logicvc->reserved_mem_base) {
|
||||
drm_err(drm_dev,
|
||||
"Framebuffer memory below reserved memory base!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fb_offset = (u32) (fb_addr - logicvc->reserved_mem_base);
|
||||
|
||||
if (fb_offset < base_offset) {
|
||||
drm_err(drm_dev,
|
||||
"Framebuffer offset below layer base offset!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gap = fb_offset - base_offset;
|
||||
|
||||
/* Use the possible video buffers selection. */
|
||||
if (gap && buffer_offset) {
|
||||
buffer_sel = gap / buffer_offset;
|
||||
if (buffer_sel > LOGICVC_BUFFER_SEL_MAX)
|
||||
buffer_sel = LOGICVC_BUFFER_SEL_MAX;
|
||||
|
||||
gap -= buffer_sel * buffer_offset;
|
||||
}
|
||||
|
||||
/* Use the vertical offset. */
|
||||
if (gap && layer_stride && logicvc->config.layers_configurable) {
|
||||
voffset = gap / layer_stride;
|
||||
if (voffset > LOGICVC_LAYER_VOFFSET_MAX)
|
||||
voffset = LOGICVC_LAYER_VOFFSET_MAX;
|
||||
|
||||
gap -= voffset * layer_stride;
|
||||
}
|
||||
|
||||
/* Use the horizontal offset. */
|
||||
if (gap && layer_bytespp && logicvc->config.layers_configurable) {
|
||||
hoffset = gap / layer_bytespp;
|
||||
if (hoffset > LOGICVC_DIMENSIONS_MAX)
|
||||
hoffset = LOGICVC_DIMENSIONS_MAX;
|
||||
|
||||
gap -= hoffset * layer_bytespp;
|
||||
}
|
||||
|
||||
if (gap) {
|
||||
drm_err(drm_dev,
|
||||
"Unable to find layer %d buffer setup for 0x%x byte gap\n",
|
||||
layer->index, fb_offset - base_offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drm_dbg_kms(drm_dev, "Found layer %d buffer setup for 0x%x byte gap:\n",
|
||||
layer->index, fb_offset - base_offset);
|
||||
|
||||
drm_dbg_kms(drm_dev, "- buffer_sel = 0x%x chunks of 0x%x bytes\n",
|
||||
buffer_sel, buffer_offset);
|
||||
drm_dbg_kms(drm_dev, "- voffset = 0x%x chunks of 0x%x bytes\n", voffset,
|
||||
layer_stride);
|
||||
drm_dbg_kms(drm_dev, "- hoffset = 0x%x chunks of 0x%x bytes\n", hoffset,
|
||||
layer_bytespp);
|
||||
|
||||
if (setup) {
|
||||
setup->buffer_sel = buffer_sel;
|
||||
setup->voffset = voffset;
|
||||
setup->hoffset = hoffset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct logicvc_layer_formats *logicvc_layer_formats_lookup(struct logicvc_layer *layer)
|
||||
{
|
||||
bool alpha;
|
||||
unsigned int i = 0;
|
||||
|
||||
alpha = (layer->config.alpha_mode == LOGICVC_LAYER_ALPHA_PIXEL);
|
||||
|
||||
while (logicvc_layer_formats[i].formats) {
|
||||
if (logicvc_layer_formats[i].colorspace == layer->config.colorspace &&
|
||||
logicvc_layer_formats[i].depth == layer->config.depth &&
|
||||
logicvc_layer_formats[i].alpha == alpha)
|
||||
return &logicvc_layer_formats[i];
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned int logicvc_layer_formats_count(struct logicvc_layer_formats *formats)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
|
||||
while (formats->formats[count] != DRM_FORMAT_INVALID)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int logicvc_layer_config_parse(struct logicvc_drm *logicvc,
|
||||
struct logicvc_layer *layer)
|
||||
{
|
||||
struct device_node *of_node = layer->of_node;
|
||||
struct logicvc_layer_config *config = &layer->config;
|
||||
int ret;
|
||||
|
||||
logicvc_of_property_parse_bool(of_node,
|
||||
LOGICVC_OF_PROPERTY_LAYER_PRIMARY,
|
||||
&config->primary);
|
||||
|
||||
ret = logicvc_of_property_parse_u32(of_node,
|
||||
LOGICVC_OF_PROPERTY_LAYER_COLORSPACE,
|
||||
&config->colorspace);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = logicvc_of_property_parse_u32(of_node,
|
||||
LOGICVC_OF_PROPERTY_LAYER_DEPTH,
|
||||
&config->depth);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = logicvc_of_property_parse_u32(of_node,
|
||||
LOGICVC_OF_PROPERTY_LAYER_ALPHA_MODE,
|
||||
&config->alpha_mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Memory offset is only relevant without layer address configuration.
|
||||
*/
|
||||
if (logicvc->caps->layer_address)
|
||||
return 0;
|
||||
|
||||
ret = logicvc_of_property_parse_u32(of_node,
|
||||
LOGICVC_OF_PROPERTY_LAYER_BASE_OFFSET,
|
||||
&config->base_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = logicvc_of_property_parse_u32(of_node,
|
||||
LOGICVC_OF_PROPERTY_LAYER_BUFFER_OFFSET,
|
||||
&config->buffer_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct logicvc_layer *logicvc_layer_get_from_index(struct logicvc_drm *logicvc,
|
||||
u32 index)
|
||||
{
|
||||
struct logicvc_layer *layer;
|
||||
|
||||
list_for_each_entry(layer, &logicvc->layers_list, list)
|
||||
if (layer->index == index)
|
||||
return layer;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct logicvc_layer *logicvc_layer_get_from_type(struct logicvc_drm *logicvc,
|
||||
enum drm_plane_type type)
|
||||
{
|
||||
struct logicvc_layer *layer;
|
||||
|
||||
list_for_each_entry(layer, &logicvc->layers_list, list)
|
||||
if (layer->drm_plane.type == type)
|
||||
return layer;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct logicvc_layer *logicvc_layer_get_primary(struct logicvc_drm *logicvc)
|
||||
{
|
||||
return logicvc_layer_get_from_type(logicvc, DRM_PLANE_TYPE_PRIMARY);
|
||||
}
|
||||
|
||||
static int logicvc_layer_init(struct logicvc_drm *logicvc,
|
||||
struct device_node *of_node, u32 index)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct device *dev = drm_dev->dev;
|
||||
struct logicvc_layer *layer = NULL;
|
||||
struct logicvc_layer_formats *formats;
|
||||
unsigned int formats_count;
|
||||
enum drm_plane_type type;
|
||||
unsigned int zpos;
|
||||
int ret;
|
||||
|
||||
layer = devm_kzalloc(dev, sizeof(*layer), GFP_KERNEL);
|
||||
if (!layer) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
layer->of_node = of_node;
|
||||
layer->index = index;
|
||||
|
||||
ret = logicvc_layer_config_parse(logicvc, layer);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to parse config for layer #%d\n",
|
||||
index);
|
||||
goto error;
|
||||
}
|
||||
|
||||
formats = logicvc_layer_formats_lookup(layer);
|
||||
if (!formats) {
|
||||
drm_err(drm_dev, "Failed to lookup formats for layer #%d\n",
|
||||
index);
|
||||
goto error;
|
||||
}
|
||||
|
||||
formats_count = logicvc_layer_formats_count(formats);
|
||||
|
||||
/* The final layer can be configured as a background layer. */
|
||||
if (logicvc->config.background_layer &&
|
||||
index == (logicvc->config.layers_count - 1)) {
|
||||
/*
|
||||
* A zero value for black is only valid for RGB, not for YUV,
|
||||
* so this will need to take the format in account for YUV.
|
||||
*/
|
||||
u32 background = 0;
|
||||
|
||||
drm_dbg_kms(drm_dev, "Using layer #%d as background layer\n",
|
||||
index);
|
||||
|
||||
regmap_write(logicvc->regmap, LOGICVC_BACKGROUND_COLOR_REG,
|
||||
background);
|
||||
|
||||
devm_kfree(dev, layer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (layer->config.primary)
|
||||
type = DRM_PLANE_TYPE_PRIMARY;
|
||||
else
|
||||
type = DRM_PLANE_TYPE_OVERLAY;
|
||||
|
||||
ret = drm_universal_plane_init(drm_dev, &layer->drm_plane, 0,
|
||||
&logicvc_plane_funcs, formats->formats,
|
||||
formats_count, NULL, type, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to initialize layer plane\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_plane_helper_add(&layer->drm_plane, &logicvc_plane_helper_funcs);
|
||||
|
||||
zpos = logicvc->config.layers_count - index - 1;
|
||||
drm_dbg_kms(drm_dev, "Giving layer #%d zpos %d\n", index, zpos);
|
||||
|
||||
if (layer->config.alpha_mode == LOGICVC_LAYER_ALPHA_LAYER)
|
||||
drm_plane_create_alpha_property(&layer->drm_plane);
|
||||
|
||||
drm_plane_create_zpos_immutable_property(&layer->drm_plane, zpos);
|
||||
|
||||
drm_dbg_kms(drm_dev, "Registering layer #%d\n", index);
|
||||
|
||||
layer->formats = formats;
|
||||
|
||||
list_add_tail(&layer->list, &logicvc->layers_list);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (layer)
|
||||
devm_kfree(dev, layer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void logicvc_layer_fini(struct logicvc_drm *logicvc,
|
||||
struct logicvc_layer *layer)
|
||||
{
|
||||
struct device *dev = logicvc->drm_dev.dev;
|
||||
|
||||
list_del(&layer->list);
|
||||
devm_kfree(dev, layer);
|
||||
}
|
||||
|
||||
void logicvc_layers_attach_crtc(struct logicvc_drm *logicvc)
|
||||
{
|
||||
uint32_t possible_crtcs = drm_crtc_mask(&logicvc->crtc->drm_crtc);
|
||||
struct logicvc_layer *layer;
|
||||
|
||||
list_for_each_entry(layer, &logicvc->layers_list, list) {
|
||||
if (layer->drm_plane.type != DRM_PLANE_TYPE_OVERLAY)
|
||||
continue;
|
||||
|
||||
layer->drm_plane.possible_crtcs = possible_crtcs;
|
||||
}
|
||||
}
|
||||
|
||||
int logicvc_layers_init(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct device *dev = drm_dev->dev;
|
||||
struct device_node *of_node = dev->of_node;
|
||||
struct device_node *layer_node = NULL;
|
||||
struct device_node *layers_node;
|
||||
struct logicvc_layer *layer;
|
||||
struct logicvc_layer *next;
|
||||
int ret = 0;
|
||||
|
||||
layers_node = of_get_child_by_name(of_node, "layers");
|
||||
if (!layers_node) {
|
||||
drm_err(drm_dev, "No layers node found in the description\n");
|
||||
ret = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for_each_child_of_node(layers_node, layer_node) {
|
||||
u32 index = 0;
|
||||
|
||||
if (!logicvc_of_node_is_layer(layer_node))
|
||||
continue;
|
||||
|
||||
ret = of_property_read_u32(layer_node, "reg", &index);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
layer = logicvc_layer_get_from_index(logicvc, index);
|
||||
if (layer) {
|
||||
drm_err(drm_dev, "Duplicated entry for layer #%d\n",
|
||||
index);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = logicvc_layer_init(logicvc, layer_node, index);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
of_node_put(layer_node);
|
||||
}
|
||||
|
||||
of_node_put(layers_node);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
list_for_each_entry_safe(layer, next, &logicvc->layers_list, list)
|
||||
logicvc_layer_fini(logicvc, layer);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef _LOGICVC_LAYER_H_
|
||||
#define _LOGICVC_LAYER_H_
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/types.h>
|
||||
#include <drm/drm_plane.h>
|
||||
|
||||
#define LOGICVC_LAYER_COLORSPACE_RGB 0
|
||||
#define LOGICVC_LAYER_COLORSPACE_YUV 1
|
||||
|
||||
#define LOGICVC_LAYER_ALPHA_LAYER 0
|
||||
#define LOGICVC_LAYER_ALPHA_PIXEL 1
|
||||
|
||||
struct logicvc_layer_buffer_setup {
|
||||
u8 buffer_sel;
|
||||
u16 voffset;
|
||||
u16 hoffset;
|
||||
};
|
||||
|
||||
struct logicvc_layer_config {
|
||||
u32 colorspace;
|
||||
u32 depth;
|
||||
u32 alpha_mode;
|
||||
u32 base_offset;
|
||||
u32 buffer_offset;
|
||||
bool primary;
|
||||
};
|
||||
|
||||
struct logicvc_layer_formats {
|
||||
u32 colorspace;
|
||||
u32 depth;
|
||||
bool alpha;
|
||||
uint32_t *formats;
|
||||
};
|
||||
|
||||
struct logicvc_layer {
|
||||
struct logicvc_layer_config config;
|
||||
struct logicvc_layer_formats *formats;
|
||||
struct device_node *of_node;
|
||||
|
||||
struct drm_plane drm_plane;
|
||||
struct list_head list;
|
||||
u32 index;
|
||||
};
|
||||
|
||||
int logicvc_layer_buffer_find_setup(struct logicvc_drm *logicvc,
|
||||
struct logicvc_layer *layer,
|
||||
struct drm_plane_state *state,
|
||||
struct logicvc_layer_buffer_setup *setup);
|
||||
struct logicvc_layer *logicvc_layer_get_from_index(struct logicvc_drm *logicvc,
|
||||
u32 index);
|
||||
struct logicvc_layer *logicvc_layer_get_from_type(struct logicvc_drm *logicvc,
|
||||
enum drm_plane_type type);
|
||||
struct logicvc_layer *logicvc_layer_get_primary(struct logicvc_drm *logicvc);
|
||||
void logicvc_layers_attach_crtc(struct logicvc_drm *logicvc);
|
||||
int logicvc_layers_init(struct logicvc_drm *logicvc);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,80 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_mode_config.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "logicvc_drm.h"
|
||||
#include "logicvc_interface.h"
|
||||
#include "logicvc_layer.h"
|
||||
#include "logicvc_mode.h"
|
||||
|
||||
static const struct drm_mode_config_funcs logicvc_mode_config_funcs = {
|
||||
.fb_create = drm_gem_fb_create,
|
||||
.output_poll_changed = drm_fb_helper_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
int logicvc_mode_init(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
struct drm_mode_config *mode_config = &drm_dev->mode_config;
|
||||
struct logicvc_layer *layer_primary;
|
||||
uint32_t preferred_depth;
|
||||
int ret;
|
||||
|
||||
ret = drm_vblank_init(drm_dev, mode_config->num_crtc);
|
||||
if (ret) {
|
||||
drm_err(drm_dev, "Failed to initialize vblank\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
layer_primary = logicvc_layer_get_primary(logicvc);
|
||||
if (!layer_primary) {
|
||||
drm_err(drm_dev, "Failed to get primary layer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
preferred_depth = layer_primary->formats->depth;
|
||||
|
||||
/* DRM counts alpha in depth, our driver doesn't. */
|
||||
if (layer_primary->formats->alpha)
|
||||
preferred_depth += 8;
|
||||
|
||||
mode_config->min_width = 64;
|
||||
mode_config->max_width = 2048;
|
||||
mode_config->min_height = 1;
|
||||
mode_config->max_height = 2048;
|
||||
mode_config->preferred_depth = preferred_depth;
|
||||
mode_config->funcs = &logicvc_mode_config_funcs;
|
||||
|
||||
drm_mode_config_reset(drm_dev);
|
||||
|
||||
drm_kms_helper_poll_init(drm_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void logicvc_mode_fini(struct logicvc_drm *logicvc)
|
||||
{
|
||||
struct drm_device *drm_dev = &logicvc->drm_dev;
|
||||
|
||||
drm_kms_helper_poll_fini(drm_dev);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef _LOGICVC_MODE_H_
|
||||
#define _LOGICVC_MODE_H_
|
||||
|
||||
struct logicvc_drm;
|
||||
|
||||
int logicvc_mode_init(struct logicvc_drm *logicvc);
|
||||
void logicvc_mode_fini(struct logicvc_drm *logicvc);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,185 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "logicvc_drm.h"
|
||||
#include "logicvc_layer.h"
|
||||
#include "logicvc_of.h"
|
||||
|
||||
static struct logicvc_of_property_sv logicvc_of_display_interface_sv[] = {
|
||||
{ "lvds-4bits", LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS },
|
||||
{ "lvds-3bits", LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct logicvc_of_property_sv logicvc_of_display_colorspace_sv[] = {
|
||||
{ "rgb", LOGICVC_DISPLAY_COLORSPACE_RGB },
|
||||
{ "yuv422", LOGICVC_DISPLAY_COLORSPACE_YUV422 },
|
||||
{ "yuv444", LOGICVC_DISPLAY_COLORSPACE_YUV444 },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct logicvc_of_property_sv logicvc_of_layer_colorspace_sv[] = {
|
||||
{ "rgb", LOGICVC_LAYER_COLORSPACE_RGB },
|
||||
{ "yuv", LOGICVC_LAYER_COLORSPACE_YUV },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct logicvc_of_property_sv logicvc_of_layer_alpha_mode_sv[] = {
|
||||
{ "layer", LOGICVC_LAYER_ALPHA_LAYER },
|
||||
{ "pixel", LOGICVC_LAYER_ALPHA_PIXEL },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct logicvc_of_property logicvc_of_properties[] = {
|
||||
[LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE] = {
|
||||
.name = "xylon,display-interface",
|
||||
.sv = logicvc_of_display_interface_sv,
|
||||
.range = {
|
||||
LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS,
|
||||
LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS,
|
||||
},
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE] = {
|
||||
.name = "xylon,display-colorspace",
|
||||
.sv = logicvc_of_display_colorspace_sv,
|
||||
.range = {
|
||||
LOGICVC_DISPLAY_COLORSPACE_RGB,
|
||||
LOGICVC_DISPLAY_COLORSPACE_YUV444,
|
||||
},
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_DISPLAY_DEPTH] = {
|
||||
.name = "xylon,display-depth",
|
||||
.range = { 8, 24 },
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_ROW_STRIDE] = {
|
||||
.name = "xylon,row-stride",
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_DITHERING] = {
|
||||
.name = "xylon,dithering",
|
||||
.optional = true,
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_BACKGROUND_LAYER] = {
|
||||
.name = "xylon,background-layer",
|
||||
.optional = true,
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE] = {
|
||||
.name = "xylon,layers-configurable",
|
||||
.optional = true,
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_LAYERS_COUNT] = {
|
||||
.name = "xylon,layers-count",
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_LAYER_DEPTH] = {
|
||||
.name = "xylon,layer-depth",
|
||||
.range = { 8, 24 },
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_LAYER_COLORSPACE] = {
|
||||
.name = "xylon,layer-colorspace",
|
||||
.sv = logicvc_of_layer_colorspace_sv,
|
||||
.range = {
|
||||
LOGICVC_LAYER_COLORSPACE_RGB,
|
||||
LOGICVC_LAYER_COLORSPACE_RGB,
|
||||
},
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_LAYER_ALPHA_MODE] = {
|
||||
.name = "xylon,layer-alpha-mode",
|
||||
.sv = logicvc_of_layer_alpha_mode_sv,
|
||||
.range = {
|
||||
LOGICVC_LAYER_ALPHA_LAYER,
|
||||
LOGICVC_LAYER_ALPHA_PIXEL,
|
||||
},
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_LAYER_BASE_OFFSET] = {
|
||||
.name = "xylon,layer-base-offset",
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_LAYER_BUFFER_OFFSET] = {
|
||||
.name = "xylon,layer-buffer-offset",
|
||||
},
|
||||
[LOGICVC_OF_PROPERTY_LAYER_PRIMARY] = {
|
||||
.name = "xylon,layer-primary",
|
||||
.optional = true,
|
||||
},
|
||||
};
|
||||
|
||||
static int logicvc_of_property_sv_value(struct logicvc_of_property_sv *sv,
|
||||
const char *string, u32 *value)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
|
||||
while (sv[i].string) {
|
||||
if (!strcmp(sv[i].string, string)) {
|
||||
*value = sv[i].value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int logicvc_of_property_parse_u32(struct device_node *of_node,
|
||||
unsigned int index, u32 *target)
|
||||
{
|
||||
struct logicvc_of_property *property;
|
||||
const char *string;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
if (index >= LOGICVC_OF_PROPERTY_MAXIMUM)
|
||||
return -EINVAL;
|
||||
|
||||
property = &logicvc_of_properties[index];
|
||||
|
||||
if (!property->optional &&
|
||||
!of_property_read_bool(of_node, property->name))
|
||||
return -ENODEV;
|
||||
|
||||
if (property->sv) {
|
||||
ret = of_property_read_string(of_node, property->name, &string);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = logicvc_of_property_sv_value(property->sv, string,
|
||||
&value);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
ret = of_property_read_u32(of_node, property->name, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (property->range[0] || property->range[1])
|
||||
if (value < property->range[0] || value > property->range[1])
|
||||
return -ERANGE;
|
||||
|
||||
*target = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void logicvc_of_property_parse_bool(struct device_node *of_node,
|
||||
unsigned int index, bool *target)
|
||||
{
|
||||
struct logicvc_of_property *property;
|
||||
|
||||
if (index >= LOGICVC_OF_PROPERTY_MAXIMUM) {
|
||||
/* Fallback. */
|
||||
*target = false;
|
||||
return;
|
||||
}
|
||||
|
||||
property = &logicvc_of_properties[index];
|
||||
*target = of_property_read_bool(of_node, property->name);
|
||||
}
|
||||
|
||||
bool logicvc_of_node_is_layer(struct device_node *of_node)
|
||||
{
|
||||
return !of_node_cmp(of_node->name, "layer");
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef _LOGICVC_OF_H_
|
||||
#define _LOGICVC_OF_H_
|
||||
|
||||
enum logicvc_of_property_index {
|
||||
LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE = 0,
|
||||
LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE,
|
||||
LOGICVC_OF_PROPERTY_DISPLAY_DEPTH,
|
||||
LOGICVC_OF_PROPERTY_ROW_STRIDE,
|
||||
LOGICVC_OF_PROPERTY_DITHERING,
|
||||
LOGICVC_OF_PROPERTY_BACKGROUND_LAYER,
|
||||
LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE,
|
||||
LOGICVC_OF_PROPERTY_LAYERS_COUNT,
|
||||
LOGICVC_OF_PROPERTY_LAYER_DEPTH,
|
||||
LOGICVC_OF_PROPERTY_LAYER_COLORSPACE,
|
||||
LOGICVC_OF_PROPERTY_LAYER_ALPHA_MODE,
|
||||
LOGICVC_OF_PROPERTY_LAYER_BASE_OFFSET,
|
||||
LOGICVC_OF_PROPERTY_LAYER_BUFFER_OFFSET,
|
||||
LOGICVC_OF_PROPERTY_LAYER_PRIMARY,
|
||||
LOGICVC_OF_PROPERTY_MAXIMUM,
|
||||
};
|
||||
|
||||
struct logicvc_of_property_sv {
|
||||
const char *string;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
struct logicvc_of_property {
|
||||
char *name;
|
||||
bool optional;
|
||||
struct logicvc_of_property_sv *sv;
|
||||
u32 range[2];
|
||||
};
|
||||
|
||||
int logicvc_of_property_parse_u32(struct device_node *of_node,
|
||||
unsigned int index, u32 *target);
|
||||
void logicvc_of_property_parse_bool(struct device_node *of_node,
|
||||
unsigned int index, bool *target);
|
||||
bool logicvc_of_node_is_layer(struct device_node *of_node);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,80 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Bootlin
|
||||
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
*
|
||||
* Copyright (C) 2014 Xylon d.o.o.
|
||||
* Author: Davor Joja <davor.joja@logicbricks.com>
|
||||
*/
|
||||
|
||||
#ifndef _LOGICVC_REGS_H_
|
||||
#define _LOGICVC_REGS_H_
|
||||
|
||||
#define LOGICVC_DIMENSIONS_MAX (BIT(16) - 1)
|
||||
|
||||
#define LOGICVC_HSYNC_FRONT_PORCH_REG 0x00
|
||||
#define LOGICVC_HSYNC_REG 0x08
|
||||
#define LOGICVC_HSYNC_BACK_PORCH_REG 0x10
|
||||
#define LOGICVC_HRES_REG 0x18
|
||||
#define LOGICVC_VSYNC_FRONT_PORCH_REG 0x20
|
||||
#define LOGICVC_VSYNC_REG 0x28
|
||||
#define LOGICVC_VSYNC_BACK_PORCH_REG 0x30
|
||||
#define LOGICVC_VRES_REG 0x38
|
||||
|
||||
#define LOGICVC_CTRL_REG 0x40
|
||||
#define LOGICVC_CTRL_CLOCK_INVERT BIT(8)
|
||||
#define LOGICVC_CTRL_PIXEL_INVERT BIT(7)
|
||||
#define LOGICVC_CTRL_DE_INVERT BIT(5)
|
||||
#define LOGICVC_CTRL_DE_ENABLE BIT(4)
|
||||
#define LOGICVC_CTRL_VSYNC_INVERT BIT(3)
|
||||
#define LOGICVC_CTRL_VSYNC_ENABLE BIT(2)
|
||||
#define LOGICVC_CTRL_HSYNC_INVERT BIT(1)
|
||||
#define LOGICVC_CTRL_HSYNC_ENABLE BIT(0)
|
||||
|
||||
#define LOGICVC_DTYPE_REG 0x48
|
||||
#define LOGICVC_BACKGROUND_COLOR_REG 0x50
|
||||
|
||||
#define LOGICVC_BUFFER_SEL_REG 0x58
|
||||
#define LOGICVC_BUFFER_SEL_VALUE(i, v) \
|
||||
(BIT(10 + (i)) | ((v) << (2 * (i))))
|
||||
#define LOGICVC_BUFFER_SEL_MAX 2
|
||||
|
||||
#define LOGICVC_DOUBLE_CLUT_REG 0x60
|
||||
|
||||
#define LOGICVC_INT_STAT_REG 0x68
|
||||
#define LOGICVC_INT_STAT_V_SYNC BIT(5)
|
||||
|
||||
#define LOGICVC_INT_MASK_REG 0x70
|
||||
#define LOGICVC_INT_MASK_V_SYNC BIT(5)
|
||||
|
||||
#define LOGICVC_POWER_CTRL_REG 0x78
|
||||
#define LOGICVC_POWER_CTRL_BACKLIGHT_ENABLE BIT(0)
|
||||
#define LOGICVC_POWER_CTRL_VDD_ENABLE BIT(1)
|
||||
#define LOGICVC_POWER_CTRL_VEE_ENABLE BIT(2)
|
||||
#define LOGICVC_POWER_CTRL_VIDEO_ENABLE BIT(3)
|
||||
|
||||
#define LOGICVC_IP_VERSION_REG 0xf8
|
||||
#define LOGICVC_IP_VERSION_MAJOR_MASK GENMASK(16, 11)
|
||||
#define LOGICVC_IP_VERSION_MINOR_MASK GENMASK(10, 5)
|
||||
#define LOGICVC_IP_VERSION_LEVEL_MASK GENMASK(4, 0)
|
||||
|
||||
#define LOGICVC_LAYER_ADDRESS_REG(i) (0x100 + (i) * 0x80)
|
||||
#define LOGICVC_LAYER_HOFFSET_REG(i) (0x100 + (i) * 0x80)
|
||||
|
||||
#define LOGICVC_LAYER_VOFFSET_REG(i) (0x108 + (i) * 0x80)
|
||||
#define LOGICVC_LAYER_VOFFSET_MAX 4095
|
||||
|
||||
#define LOGICVC_LAYER_HPOSITION_REG(i) (0x110 + (i) * 0x80)
|
||||
#define LOGICVC_LAYER_VPOSITION_REG(i) (0x118 + (i) * 0x80)
|
||||
#define LOGICVC_LAYER_WIDTH_REG(i) (0x120 + (i) * 0x80)
|
||||
#define LOGICVC_LAYER_HEIGHT_REG(i) (0x128 + (i) * 0x80)
|
||||
#define LOGICVC_LAYER_ALPHA_REG(i) (0x130 + (i) * 0x80)
|
||||
|
||||
#define LOGICVC_LAYER_CTRL_REG(i) (0x138 + (i) * 0x80)
|
||||
#define LOGICVC_LAYER_CTRL_ENABLE BIT(0)
|
||||
#define LOGICVC_LAYER_CTRL_COLOR_KEY_DISABLE BIT(1)
|
||||
#define LOGICVC_LAYER_CTRL_PIXEL_FORMAT_INVERT BIT(4)
|
||||
|
||||
#define LOGICVC_LAYER_COLOR_KEY_REG(i) (0x140 + (i) * 0x80)
|
||||
|
||||
#endif
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <drm/drm_aperture.h>
|
||||
#include <drm/drm_drv.h>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
|
||||
|
|
|
@ -102,14 +102,12 @@ static int dp_parser_ctrl_res(struct dp_parser *parser)
|
|||
static int dp_parser_misc(struct dp_parser *parser)
|
||||
{
|
||||
struct device_node *of_node = parser->pdev->dev.of_node;
|
||||
int len = 0;
|
||||
const char *data_lane_property = "data-lanes";
|
||||
int len;
|
||||
|
||||
len = of_property_count_elems_of_size(of_node,
|
||||
data_lane_property, sizeof(u32));
|
||||
len = drm_of_get_data_lanes_count(of_node, 1, DP_MAX_NUM_DP_LANES);
|
||||
if (len < 0) {
|
||||
DRM_WARN("Invalid property %s, default max DP lanes = %d\n",
|
||||
data_lane_property, DP_MAX_NUM_DP_LANES);
|
||||
DRM_WARN("Invalid property \"data-lanes\", default max DP lanes = %d\n",
|
||||
DP_MAX_NUM_DP_LANES);
|
||||
len = DP_MAX_NUM_DP_LANES;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#include <drm/drm_of.h>
|
||||
|
||||
#include "dsi.h"
|
||||
#include "dsi.xml.h"
|
||||
#include "sfpb.xml.h"
|
||||
|
@ -1779,11 +1781,10 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host,
|
|||
return 0;
|
||||
}
|
||||
|
||||
num_lanes = len / sizeof(u32);
|
||||
|
||||
if (num_lanes < 1 || num_lanes > 4) {
|
||||
num_lanes = drm_of_get_data_lanes_count(ep, 1, 4);
|
||||
if (num_lanes < 0) {
|
||||
DRM_DEV_ERROR(dev, "bad number of data lanes\n");
|
||||
return -EINVAL;
|
||||
return num_lanes;
|
||||
}
|
||||
|
||||
msm_host->num_data_lanes = num_lanes;
|
||||
|
|
|
@ -190,7 +190,7 @@
|
|||
#define DRF_MD_(X,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,IMPL,...) IMPL
|
||||
#define DRF_MD(A...) DRF_MD_(X, ##A, DRF_MD_I, DRF_MD_N)(X, ##A)
|
||||
|
||||
/* Helper for testing against field value in aribtrary object */
|
||||
/* Helper for testing against field value in arbitrary object */
|
||||
#define DRF_TV_N(X,e,p,o,d,r, f,cmp,v) \
|
||||
NVVAL_TEST_X(DRF_RD_X(e, (p), (o), d##_##r ), d##_##r##_##f, cmp, (v))
|
||||
#define DRF_TV_I(X,e,p,o,d,r,i,f,cmp,v) \
|
||||
|
@ -198,7 +198,7 @@
|
|||
#define DRF_TV_(X,_1,_2,_3,_4,_5,_6,_7,_8,_9,IMPL,...) IMPL
|
||||
#define DRF_TV(A...) DRF_TV_(X, ##A, DRF_TV_I, DRF_TV_N)(X, ##A)
|
||||
|
||||
/* Helper for testing against field definition in aribtrary object */
|
||||
/* Helper for testing against field definition in arbitrary object */
|
||||
#define DRF_TD_N(X,e,p,o,d,r, f,cmp,v) \
|
||||
NVVAL_TEST_X(DRF_RD_X(e, (p), (o), d##_##r ), d##_##r##_##f, cmp, d##_##r##_##f##_##v)
|
||||
#define DRF_TD_I(X,e,p,o,d,r,i,f,cmp,v) \
|
||||
|
|
|
@ -19,7 +19,7 @@ struct nvbios_source {
|
|||
int nvbios_extend(struct nvkm_bios *, u32 length);
|
||||
int nvbios_shadow(struct nvkm_bios *);
|
||||
|
||||
extern const struct nvbios_source nvbios_rom;
|
||||
extern const struct nvbios_source nvbios_prom;
|
||||
extern const struct nvbios_source nvbios_ramin;
|
||||
extern const struct nvbios_source nvbios_acpi_fast;
|
||||
extern const struct nvbios_source nvbios_acpi_slow;
|
||||
|
|
|
@ -171,7 +171,7 @@ nvbios_shadow(struct nvkm_bios *bios)
|
|||
struct shadow mthds[] = {
|
||||
{ 0, &nvbios_of },
|
||||
{ 0, &nvbios_ramin },
|
||||
{ 0, &nvbios_rom },
|
||||
{ 0, &nvbios_prom },
|
||||
{ 0, &nvbios_acpi_fast },
|
||||
{ 4, &nvbios_acpi_slow },
|
||||
{ 1, &nvbios_pcirom },
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <subdev/pci.h>
|
||||
|
||||
static u32
|
||||
prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
|
||||
nvbios_prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
|
||||
{
|
||||
struct nvkm_device *device = data;
|
||||
u32 i;
|
||||
|
@ -38,14 +38,14 @@ prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
|
|||
}
|
||||
|
||||
static void
|
||||
prom_fini(void *data)
|
||||
nvbios_prom_fini(void *data)
|
||||
{
|
||||
struct nvkm_device *device = data;
|
||||
nvkm_pci_rom_shadow(device->pci, true);
|
||||
}
|
||||
|
||||
static void *
|
||||
prom_init(struct nvkm_bios *bios, const char *name)
|
||||
nvbios_prom_init(struct nvkm_bios *bios, const char *name)
|
||||
{
|
||||
struct nvkm_device *device = bios->subdev.device;
|
||||
if (device->card_type == NV_40 && device->chipset >= 0x4c)
|
||||
|
@ -55,10 +55,10 @@ prom_init(struct nvkm_bios *bios, const char *name)
|
|||
}
|
||||
|
||||
const struct nvbios_source
|
||||
nvbios_rom = {
|
||||
nvbios_prom = {
|
||||
.name = "PROM",
|
||||
.init = prom_init,
|
||||
.fini = prom_fini,
|
||||
.read = prom_read,
|
||||
.init = nvbios_prom_init,
|
||||
.fini = nvbios_prom_fini,
|
||||
.read = nvbios_prom_read,
|
||||
.rw = false,
|
||||
};
|
||||
|
|
|
@ -628,6 +628,10 @@ static int nt36672a_panel_add(struct nt36672a_panel *pinfo)
|
|||
|
||||
drm_panel_init(&pinfo->base, dev, &panel_funcs, DRM_MODE_CONNECTOR_DSI);
|
||||
|
||||
ret = drm_panel_of_backlight(&pinfo->base);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get backlight\n");
|
||||
|
||||
drm_panel_add(&pinfo->base);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
#include "qxl_drv.h"
|
||||
#include "qxl_object.h"
|
||||
|
||||
int qxl_log_level;
|
||||
|
||||
static bool qxl_check_device(struct qxl_device *qdev)
|
||||
{
|
||||
struct qxl_rom *rom = qdev->rom;
|
||||
|
|
|
@ -679,23 +679,12 @@ static const struct mipi_dsi_host_ops rcar_mipi_dsi_host_ops = {
|
|||
|
||||
static int rcar_mipi_dsi_parse_dt(struct rcar_mipi_dsi *dsi)
|
||||
{
|
||||
struct device_node *ep;
|
||||
u32 data_lanes[4];
|
||||
int ret;
|
||||
|
||||
ep = of_graph_get_endpoint_by_regs(dsi->dev->of_node, 1, 0);
|
||||
if (!ep) {
|
||||
dev_dbg(dsi->dev, "unconnected port@1\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_property_read_variable_u32_array(ep, "data-lanes", data_lanes,
|
||||
1, 4);
|
||||
of_node_put(ep);
|
||||
|
||||
ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4);
|
||||
if (ret < 0) {
|
||||
dev_err(dsi->dev, "missing or invalid data-lanes property\n");
|
||||
return -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dsi->num_data_lanes = ret;
|
||||
|
|
|
@ -151,16 +151,11 @@ struct sun8i_hdmi_phy;
|
|||
struct sun8i_hdmi_phy_variant {
|
||||
bool has_phy_clk;
|
||||
bool has_second_pll;
|
||||
unsigned int is_custom_phy : 1;
|
||||
const struct dw_hdmi_curr_ctrl *cur_ctr;
|
||||
const struct dw_hdmi_mpll_config *mpll_cfg;
|
||||
const struct dw_hdmi_phy_config *phy_cfg;
|
||||
const struct dw_hdmi_phy_ops *phy_ops;
|
||||
void (*phy_init)(struct sun8i_hdmi_phy *phy);
|
||||
void (*phy_disable)(struct dw_hdmi *hdmi,
|
||||
struct sun8i_hdmi_phy *phy);
|
||||
int (*phy_config)(struct dw_hdmi *hdmi,
|
||||
struct sun8i_hdmi_phy *phy,
|
||||
unsigned int clk_rate);
|
||||
};
|
||||
|
||||
struct sun8i_hdmi_phy {
|
||||
|
@ -173,7 +168,7 @@ struct sun8i_hdmi_phy {
|
|||
unsigned int rcal;
|
||||
struct regmap *regs;
|
||||
struct reset_control *rst_phy;
|
||||
struct sun8i_hdmi_phy_variant *variant;
|
||||
const struct sun8i_hdmi_phy_variant *variant;
|
||||
};
|
||||
|
||||
struct sun8i_dw_hdmi_quirks {
|
||||
|
|
|
@ -123,10 +123,30 @@ static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = {
|
|||
{ ~0UL, 0x0000, 0x0000, 0x0000}
|
||||
};
|
||||
|
||||
static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi,
|
||||
struct sun8i_hdmi_phy *phy,
|
||||
unsigned int clk_rate)
|
||||
static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
|
||||
|
||||
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
|
||||
SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
|
||||
};
|
||||
|
||||
static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
|
||||
const struct drm_display_info *display,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
unsigned int clk_rate = mode->crtc_clock * 1000;
|
||||
struct sun8i_hdmi_phy *phy = data;
|
||||
|
||||
sun8i_hdmi_phy_set_polarity(phy, mode);
|
||||
|
||||
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
|
||||
SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
|
||||
SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
|
||||
|
@ -185,10 +205,31 @@ static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
|
||||
struct sun8i_hdmi_phy *phy,
|
||||
unsigned int clk_rate)
|
||||
static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
|
||||
{
|
||||
struct sun8i_hdmi_phy *phy = data;
|
||||
|
||||
dw_hdmi_phy_gen2_txpwron(hdmi, 0);
|
||||
dw_hdmi_phy_gen2_pddq(hdmi, 1);
|
||||
|
||||
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
|
||||
SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
|
||||
}
|
||||
|
||||
static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = {
|
||||
.init = sun8i_a83t_hdmi_phy_config,
|
||||
.disable = sun8i_a83t_hdmi_phy_disable,
|
||||
.read_hpd = dw_hdmi_phy_read_hpd,
|
||||
.update_hpd = dw_hdmi_phy_update_hpd,
|
||||
.setup_hpd = dw_hdmi_phy_setup_hpd,
|
||||
};
|
||||
|
||||
static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
|
||||
const struct drm_display_info *display,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
unsigned int clk_rate = mode->crtc_clock * 1000;
|
||||
struct sun8i_hdmi_phy *phy = data;
|
||||
u32 pll_cfg1_init;
|
||||
u32 pll_cfg2_init;
|
||||
u32 ana_cfg1_end;
|
||||
|
@ -197,6 +238,11 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
|
|||
u32 b_offset = 0;
|
||||
u32 val;
|
||||
|
||||
if (phy->variant->has_phy_clk)
|
||||
clk_set_rate(phy->clk_phy, clk_rate);
|
||||
|
||||
sun8i_hdmi_phy_set_polarity(phy, mode);
|
||||
|
||||
/* bandwidth / frequency independent settings */
|
||||
|
||||
pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
|
||||
|
@ -333,41 +379,10 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
|
||||
const struct drm_display_info *display,
|
||||
const struct drm_display_mode *mode)
|
||||
static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
|
||||
{
|
||||
struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
|
||||
u32 val = 0;
|
||||
struct sun8i_hdmi_phy *phy = data;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
|
||||
|
||||
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
|
||||
SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
|
||||
|
||||
if (phy->variant->has_phy_clk)
|
||||
clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000);
|
||||
|
||||
return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000);
|
||||
};
|
||||
|
||||
static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi,
|
||||
struct sun8i_hdmi_phy *phy)
|
||||
{
|
||||
dw_hdmi_phy_gen2_txpwron(hdmi, 0);
|
||||
dw_hdmi_phy_gen2_pddq(hdmi, 1);
|
||||
|
||||
regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
|
||||
SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
|
||||
}
|
||||
|
||||
static void sun8i_hdmi_phy_disable_h3(struct dw_hdmi *hdmi,
|
||||
struct sun8i_hdmi_phy *phy)
|
||||
{
|
||||
regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
|
||||
SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
|
||||
SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
|
||||
|
@ -375,19 +390,12 @@ static void sun8i_hdmi_phy_disable_h3(struct dw_hdmi *hdmi,
|
|||
regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0);
|
||||
}
|
||||
|
||||
static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
|
||||
{
|
||||
struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
|
||||
|
||||
phy->variant->phy_disable(hdmi, phy);
|
||||
}
|
||||
|
||||
static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = {
|
||||
.init = &sun8i_hdmi_phy_config,
|
||||
.disable = &sun8i_hdmi_phy_disable,
|
||||
.read_hpd = &dw_hdmi_phy_read_hpd,
|
||||
.update_hpd = &dw_hdmi_phy_update_hpd,
|
||||
.setup_hpd = &dw_hdmi_phy_setup_hpd,
|
||||
static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = {
|
||||
.init = sun8i_h3_hdmi_phy_config,
|
||||
.disable = sun8i_h3_hdmi_phy_disable,
|
||||
.read_hpd = dw_hdmi_phy_read_hpd,
|
||||
.update_hpd = dw_hdmi_phy_update_hpd,
|
||||
.setup_hpd = dw_hdmi_phy_setup_hpd,
|
||||
};
|
||||
|
||||
static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy)
|
||||
|
@ -565,10 +573,10 @@ void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
|
|||
void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
|
||||
struct dw_hdmi_plat_data *plat_data)
|
||||
{
|
||||
struct sun8i_hdmi_phy_variant *variant = phy->variant;
|
||||
const struct sun8i_hdmi_phy_variant *variant = phy->variant;
|
||||
|
||||
if (variant->is_custom_phy) {
|
||||
plat_data->phy_ops = &sun8i_hdmi_phy_ops;
|
||||
if (variant->phy_ops) {
|
||||
plat_data->phy_ops = variant->phy_ops;
|
||||
plat_data->phy_name = "sun8i_dw_hdmi_phy";
|
||||
plat_data->phy_data = phy;
|
||||
} else {
|
||||
|
@ -587,35 +595,27 @@ static const struct regmap_config sun8i_hdmi_phy_regmap_config = {
|
|||
};
|
||||
|
||||
static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
|
||||
.is_custom_phy = true,
|
||||
.phy_ops = &sun8i_a83t_hdmi_phy_ops,
|
||||
.phy_init = &sun8i_hdmi_phy_init_a83t,
|
||||
.phy_disable = &sun8i_hdmi_phy_disable_a83t,
|
||||
.phy_config = &sun8i_hdmi_phy_config_a83t,
|
||||
};
|
||||
|
||||
static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
|
||||
.has_phy_clk = true,
|
||||
.is_custom_phy = true,
|
||||
.phy_ops = &sun8i_h3_hdmi_phy_ops,
|
||||
.phy_init = &sun8i_hdmi_phy_init_h3,
|
||||
.phy_disable = &sun8i_hdmi_phy_disable_h3,
|
||||
.phy_config = &sun8i_hdmi_phy_config_h3,
|
||||
};
|
||||
|
||||
static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = {
|
||||
.has_phy_clk = true,
|
||||
.has_second_pll = true,
|
||||
.is_custom_phy = true,
|
||||
.phy_ops = &sun8i_h3_hdmi_phy_ops,
|
||||
.phy_init = &sun8i_hdmi_phy_init_h3,
|
||||
.phy_disable = &sun8i_hdmi_phy_disable_h3,
|
||||
.phy_config = &sun8i_hdmi_phy_config_h3,
|
||||
};
|
||||
|
||||
static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = {
|
||||
.has_phy_clk = true,
|
||||
.is_custom_phy = true,
|
||||
.phy_ops = &sun8i_h3_hdmi_phy_ops,
|
||||
.phy_init = &sun8i_hdmi_phy_init_h3,
|
||||
.phy_disable = &sun8i_hdmi_phy_disable_h3,
|
||||
.phy_config = &sun8i_hdmi_phy_config_h3,
|
||||
};
|
||||
|
||||
static const struct sun8i_hdmi_phy_variant sun50i_h6_hdmi_phy = {
|
||||
|
@ -672,116 +672,64 @@ int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
|
|||
|
||||
static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct sun8i_hdmi_phy *phy;
|
||||
struct resource res;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
match = of_match_node(sun8i_hdmi_phy_of_table, node);
|
||||
if (!match) {
|
||||
dev_err(dev, "Incompatible HDMI PHY\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->variant = (struct sun8i_hdmi_phy_variant *)match->data;
|
||||
phy->variant = of_device_get_match_data(dev);
|
||||
phy->dev = dev;
|
||||
|
||||
ret = of_address_to_resource(node, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(dev, "phy: Couldn't get our resources\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regs = devm_ioremap_resource(dev, &res);
|
||||
if (IS_ERR(regs)) {
|
||||
dev_err(dev, "Couldn't map the HDMI PHY registers\n");
|
||||
return PTR_ERR(regs);
|
||||
}
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(regs))
|
||||
return dev_err_probe(dev, PTR_ERR(regs),
|
||||
"Couldn't map the HDMI PHY registers\n");
|
||||
|
||||
phy->regs = devm_regmap_init_mmio(dev, regs,
|
||||
&sun8i_hdmi_phy_regmap_config);
|
||||
if (IS_ERR(phy->regs)) {
|
||||
dev_err(dev, "Couldn't create the HDMI PHY regmap\n");
|
||||
return PTR_ERR(phy->regs);
|
||||
}
|
||||
if (IS_ERR(phy->regs))
|
||||
return dev_err_probe(dev, PTR_ERR(phy->regs),
|
||||
"Couldn't create the HDMI PHY regmap\n");
|
||||
|
||||
phy->clk_bus = of_clk_get_by_name(node, "bus");
|
||||
if (IS_ERR(phy->clk_bus)) {
|
||||
dev_err(dev, "Could not get bus clock\n");
|
||||
return PTR_ERR(phy->clk_bus);
|
||||
}
|
||||
phy->clk_bus = devm_clk_get(dev, "bus");
|
||||
if (IS_ERR(phy->clk_bus))
|
||||
return dev_err_probe(dev, PTR_ERR(phy->clk_bus),
|
||||
"Could not get bus clock\n");
|
||||
|
||||
phy->clk_mod = of_clk_get_by_name(node, "mod");
|
||||
if (IS_ERR(phy->clk_mod)) {
|
||||
dev_err(dev, "Could not get mod clock\n");
|
||||
ret = PTR_ERR(phy->clk_mod);
|
||||
goto err_put_clk_bus;
|
||||
}
|
||||
phy->clk_mod = devm_clk_get(dev, "mod");
|
||||
if (IS_ERR(phy->clk_mod))
|
||||
return dev_err_probe(dev, PTR_ERR(phy->clk_mod),
|
||||
"Could not get mod clock\n");
|
||||
|
||||
if (phy->variant->has_phy_clk) {
|
||||
phy->clk_pll0 = of_clk_get_by_name(node, "pll-0");
|
||||
if (IS_ERR(phy->clk_pll0)) {
|
||||
dev_err(dev, "Could not get pll-0 clock\n");
|
||||
ret = PTR_ERR(phy->clk_pll0);
|
||||
goto err_put_clk_mod;
|
||||
}
|
||||
phy->clk_pll0 = devm_clk_get(dev, "pll-0");
|
||||
if (IS_ERR(phy->clk_pll0))
|
||||
return dev_err_probe(dev, PTR_ERR(phy->clk_pll0),
|
||||
"Could not get pll-0 clock\n");
|
||||
|
||||
if (phy->variant->has_second_pll) {
|
||||
phy->clk_pll1 = of_clk_get_by_name(node, "pll-1");
|
||||
if (IS_ERR(phy->clk_pll1)) {
|
||||
dev_err(dev, "Could not get pll-1 clock\n");
|
||||
ret = PTR_ERR(phy->clk_pll1);
|
||||
goto err_put_clk_pll0;
|
||||
}
|
||||
phy->clk_pll1 = devm_clk_get(dev, "pll-1");
|
||||
if (IS_ERR(phy->clk_pll1))
|
||||
return dev_err_probe(dev, PTR_ERR(phy->clk_pll1),
|
||||
"Could not get pll-1 clock\n");
|
||||
}
|
||||
}
|
||||
|
||||
phy->rst_phy = of_reset_control_get_shared(node, "phy");
|
||||
if (IS_ERR(phy->rst_phy)) {
|
||||
dev_err(dev, "Could not get phy reset control\n");
|
||||
ret = PTR_ERR(phy->rst_phy);
|
||||
goto err_put_clk_pll1;
|
||||
}
|
||||
phy->rst_phy = devm_reset_control_get_shared(dev, "phy");
|
||||
if (IS_ERR(phy->rst_phy))
|
||||
return dev_err_probe(dev, PTR_ERR(phy->rst_phy),
|
||||
"Could not get phy reset control\n");
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_clk_pll1:
|
||||
clk_put(phy->clk_pll1);
|
||||
err_put_clk_pll0:
|
||||
clk_put(phy->clk_pll0);
|
||||
err_put_clk_mod:
|
||||
clk_put(phy->clk_mod);
|
||||
err_put_clk_bus:
|
||||
clk_put(phy->clk_bus);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun8i_hdmi_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sun8i_hdmi_phy *phy = platform_get_drvdata(pdev);
|
||||
|
||||
reset_control_put(phy->rst_phy);
|
||||
|
||||
clk_put(phy->clk_pll0);
|
||||
clk_put(phy->clk_pll1);
|
||||
clk_put(phy->clk_mod);
|
||||
clk_put(phy->clk_bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver sun8i_hdmi_phy_driver = {
|
||||
.probe = sun8i_hdmi_phy_probe,
|
||||
.remove = sun8i_hdmi_phy_remove,
|
||||
.driver = {
|
||||
.name = "sun8i-hdmi-phy",
|
||||
.of_match_table = sun8i_hdmi_phy_of_table,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <drm/drm_aperture.h>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config DRM_V3D
|
||||
tristate "Broadcom V3D 3.x and newer"
|
||||
depends on ARCH_BCM || ARCH_BRCMSTB || COMPILE_TEST
|
||||
depends on ARCH_BCM || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
|
||||
depends on DRM
|
||||
depends on COMMON_CLK
|
||||
depends on MMU
|
||||
|
@ -9,4 +9,5 @@ config DRM_V3D
|
|||
select DRM_GEM_SHMEM_HELPER
|
||||
help
|
||||
Choose this option if you have a system that has a Broadcom
|
||||
V3D 3.x or newer GPU, such as BCM7268.
|
||||
V3D 3.x or newer GPUs. SoCs supported include the BCM2711,
|
||||
BCM7268 and BCM7278.
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <linux/circ_buf.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
|
@ -131,11 +130,7 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused)
|
|||
struct drm_device *dev = node->minor->dev;
|
||||
struct v3d_dev *v3d = to_v3d_dev(dev);
|
||||
u32 ident0, ident1, ident2, ident3, cores;
|
||||
int ret, core;
|
||||
|
||||
ret = pm_runtime_get_sync(v3d->drm.dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
int core;
|
||||
|
||||
ident0 = V3D_READ(V3D_HUB_IDENT0);
|
||||
ident1 = V3D_READ(V3D_HUB_IDENT1);
|
||||
|
@ -188,9 +183,6 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused)
|
|||
(misccfg & V3D_MISCCFG_OVRTMUOUT) != 0);
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(v3d->drm.dev);
|
||||
pm_runtime_put_autosuspend(v3d->drm.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -218,11 +210,6 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
|
|||
uint32_t cycles;
|
||||
int core = 0;
|
||||
int measure_ms = 1000;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(v3d->drm.dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (v3d->ver >= 40) {
|
||||
V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3,
|
||||
|
@ -246,9 +233,6 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
|
|||
cycles / (measure_ms * 1000),
|
||||
(cycles / (measure_ms * 100)) % 10);
|
||||
|
||||
pm_runtime_mark_last_busy(v3d->drm.dev);
|
||||
pm_runtime_put_autosuspend(v3d->drm.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
|
@ -43,7 +42,6 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data,
|
|||
{
|
||||
struct v3d_dev *v3d = to_v3d_dev(dev);
|
||||
struct drm_v3d_get_param *args = data;
|
||||
int ret;
|
||||
static const u32 reg_map[] = {
|
||||
[DRM_V3D_PARAM_V3D_UIFCFG] = V3D_HUB_UIFCFG,
|
||||
[DRM_V3D_PARAM_V3D_HUB_IDENT1] = V3D_HUB_IDENT1,
|
||||
|
@ -69,17 +67,12 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data,
|
|||
if (args->value != 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = pm_runtime_get_sync(v3d->drm.dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (args->param >= DRM_V3D_PARAM_V3D_CORE0_IDENT0 &&
|
||||
args->param <= DRM_V3D_PARAM_V3D_CORE0_IDENT2) {
|
||||
args->value = V3D_CORE_READ(0, offset);
|
||||
} else {
|
||||
args->value = V3D_READ(offset);
|
||||
}
|
||||
pm_runtime_mark_last_busy(v3d->drm.dev);
|
||||
pm_runtime_put_autosuspend(v3d->drm.dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -198,6 +191,7 @@ static const struct drm_driver v3d_drm_driver = {
|
|||
};
|
||||
|
||||
static const struct of_device_id v3d_of_match[] = {
|
||||
{ .compatible = "brcm,2711-v3d" },
|
||||
{ .compatible = "brcm,7268-v3d" },
|
||||
{ .compatible = "brcm,7278-v3d" },
|
||||
{},
|
||||
|
@ -280,10 +274,6 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 50);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = v3d_gem_init(drm);
|
||||
if (ret)
|
||||
goto dma_free;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
@ -372,9 +371,6 @@ v3d_job_free(struct kref *ref)
|
|||
dma_fence_put(job->irq_fence);
|
||||
dma_fence_put(job->done_fence);
|
||||
|
||||
pm_runtime_mark_last_busy(job->v3d->drm.dev);
|
||||
pm_runtime_put_autosuspend(job->v3d->drm.dev);
|
||||
|
||||
if (job->perfmon)
|
||||
v3d_perfmon_put(job->perfmon);
|
||||
|
||||
|
@ -476,14 +472,10 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
|
|||
job->v3d = v3d;
|
||||
job->free = free;
|
||||
|
||||
ret = pm_runtime_get_sync(v3d->drm.dev);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue],
|
||||
v3d_priv);
|
||||
if (ret)
|
||||
goto fail_job;
|
||||
goto fail;
|
||||
|
||||
if (has_multisync) {
|
||||
if (se->in_sync_count && se->wait_stage == queue) {
|
||||
|
@ -514,8 +506,6 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
|
|||
|
||||
fail_deps:
|
||||
drm_sched_job_cleanup(&job->base);
|
||||
fail_job:
|
||||
pm_runtime_put_autosuspend(v3d->drm.dev);
|
||||
fail:
|
||||
kfree(*container);
|
||||
*container = NULL;
|
||||
|
|
|
@ -580,8 +580,10 @@ static int virtio_gpu_get_caps_ioctl(struct drm_device *dev,
|
|||
spin_unlock(&vgdev->display_info_lock);
|
||||
|
||||
/* not in cache - need to talk to hw */
|
||||
virtio_gpu_cmd_get_capset(vgdev, found_valid, args->cap_set_ver,
|
||||
&cache_ent);
|
||||
ret = virtio_gpu_cmd_get_capset(vgdev, found_valid, args->cap_set_ver,
|
||||
&cache_ent);
|
||||
if (ret)
|
||||
return ret;
|
||||
virtio_gpu_notify(vgdev);
|
||||
|
||||
copy_exit:
|
||||
|
|
|
@ -168,9 +168,9 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
|
|||
* since virtio_gpu doesn't support dma-buf import from other devices.
|
||||
*/
|
||||
shmem->pages = drm_gem_shmem_get_sg_table(&bo->base);
|
||||
if (!shmem->pages) {
|
||||
if (IS_ERR(shmem->pages)) {
|
||||
drm_gem_shmem_unpin(&bo->base);
|
||||
return -EINVAL;
|
||||
return PTR_ERR(shmem->pages);
|
||||
}
|
||||
|
||||
if (use_dma_api) {
|
||||
|
|
|
@ -90,7 +90,6 @@ static const struct virtio_dma_buf_ops virtgpu_dmabuf_ops = {
|
|||
int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev,
|
||||
struct virtio_gpu_object *bo)
|
||||
{
|
||||
int ret;
|
||||
struct virtio_gpu_object_array *objs;
|
||||
|
||||
objs = virtio_gpu_array_alloc(1);
|
||||
|
@ -98,11 +97,8 @@ int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev,
|
|||
return -ENOMEM;
|
||||
|
||||
virtio_gpu_array_add_obj(objs, &bo->base.base);
|
||||
ret = virtio_gpu_cmd_resource_assign_uuid(vgdev, objs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return virtio_gpu_cmd_resource_assign_uuid(vgdev, objs);
|
||||
}
|
||||
|
||||
struct dma_buf *virtgpu_gem_prime_export(struct drm_gem_object *obj,
|
||||
|
|
|
@ -157,7 +157,7 @@ static void compose_plane(struct vkms_composer *primary_composer,
|
|||
void *vaddr;
|
||||
void (*pixel_blend)(const u8 *p_src, u8 *p_dst);
|
||||
|
||||
if (WARN_ON(iosys_map_is_null(&primary_composer->map[0])))
|
||||
if (WARN_ON(iosys_map_is_null(&plane_composer->map[0])))
|
||||
return;
|
||||
|
||||
vaddr = plane_composer->map[0].vaddr;
|
||||
|
@ -180,7 +180,7 @@ static int compose_active_planes(void **vaddr_out,
|
|||
int i;
|
||||
|
||||
if (!*vaddr_out) {
|
||||
*vaddr_out = kzalloc(gem_obj->size, GFP_KERNEL);
|
||||
*vaddr_out = kvzalloc(gem_obj->size, GFP_KERNEL);
|
||||
if (!*vaddr_out) {
|
||||
DRM_ERROR("Cannot allocate memory for output frame.");
|
||||
return -ENOMEM;
|
||||
|
@ -263,7 +263,7 @@ void vkms_composer_worker(struct work_struct *work)
|
|||
crtc_state);
|
||||
if (ret) {
|
||||
if (ret == -EINVAL && !wb_pending)
|
||||
kfree(vaddr_out);
|
||||
kvfree(vaddr_out);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -275,7 +275,7 @@ void vkms_composer_worker(struct work_struct *work)
|
|||
crtc_state->wb_pending = false;
|
||||
spin_unlock_irq(&out->composer_lock);
|
||||
} else {
|
||||
kfree(vaddr_out);
|
||||
kvfree(vaddr_out);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -616,7 +616,7 @@ config FB_UVESA
|
|||
|
||||
config FB_VESA
|
||||
bool "VESA VGA graphics support"
|
||||
depends on (FB = y) && (X86 || COMPILE_TEST)
|
||||
depends on (FB = y) && X86
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
|
|
|
@ -2182,7 +2182,6 @@ static int fbcon_switch(struct vc_data *vc)
|
|||
if (logo_shown == FBCON_LOGO_DRAW) {
|
||||
|
||||
logo_shown = fg_console;
|
||||
/* This is protected above by initmem_freed */
|
||||
fb_show_logo(info, ops->rotate);
|
||||
update_region(vc,
|
||||
vc->vc_origin + vc->vc_size_row * vc->vc_top,
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfb.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/vt.h>
|
||||
|
@ -1764,6 +1765,17 @@ int remove_conflicting_framebuffers(struct apertures_struct *a,
|
|||
do_free = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a driver asked to unregister a platform device registered by
|
||||
* sysfb, then can be assumed that this is a driver for a display
|
||||
* that is set up by the system firmware and has a generic driver.
|
||||
*
|
||||
* Drivers for devices that don't have a generic driver will never
|
||||
* ask for this, so let's assume that a real driver for the display
|
||||
* was already probed and prevent sysfb to register devices later.
|
||||
*/
|
||||
sysfb_disable();
|
||||
|
||||
mutex_lock(®istration_lock);
|
||||
do_remove_conflicting_framebuffers(a, name, primary);
|
||||
mutex_unlock(®istration_lock);
|
||||
|
|
|
@ -358,17 +358,6 @@ static int efifb_probe(struct platform_device *dev)
|
|||
char *option = NULL;
|
||||
efi_memory_desc_t md;
|
||||
|
||||
/*
|
||||
* Generic drivers must not be registered if a framebuffer exists.
|
||||
* If a native driver was probed, the display hardware was already
|
||||
* taken and attempting to use the system framebuffer is dangerous.
|
||||
*/
|
||||
if (num_registered_fb > 0) {
|
||||
dev_err(&dev->dev,
|
||||
"efifb: a framebuffer is already registered\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
@ -419,17 +419,6 @@ static int simplefb_probe(struct platform_device *pdev)
|
|||
struct simplefb_par *par;
|
||||
struct resource *res, *mem;
|
||||
|
||||
/*
|
||||
* Generic drivers must not be registered if a framebuffer exists.
|
||||
* If a native driver was probed, the display hardware was already
|
||||
* taken and attempting to use the system framebuffer is dangerous.
|
||||
*/
|
||||
if (num_registered_fb > 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"simplefb: a framebuffer is already registered\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fb_get_options("simplefb", NULL))
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
@ -372,8 +372,10 @@ struct drm_dp_aux {
|
|||
* Also note that this callback can be called no matter the
|
||||
* state @dev is in and also no matter what state the panel is
|
||||
* in. It's expected:
|
||||
*
|
||||
* - If the @dev providing the AUX bus is currently unpowered then
|
||||
* it will power itself up for the transfer.
|
||||
*
|
||||
* - If we're on eDP (using a drm_panel) and the panel is not in a
|
||||
* state where it can respond (it's not powered or it's in a
|
||||
* low power state) then this function may return an error, but
|
||||
|
|
|
@ -594,6 +594,15 @@ drm_display_mode_from_cea_vic(struct drm_device *dev,
|
|||
u8 video_code);
|
||||
|
||||
/* Interface based on struct drm_edid */
|
||||
const struct drm_edid *drm_edid_alloc(const void *edid, size_t size);
|
||||
const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid);
|
||||
void drm_edid_free(const struct drm_edid *drm_edid);
|
||||
const struct drm_edid *drm_edid_read(struct drm_connector *connector);
|
||||
const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector,
|
||||
struct i2c_adapter *adapter);
|
||||
const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector,
|
||||
int (*read_block)(void *context, u8 *buf, unsigned int block, size_t len),
|
||||
void *context);
|
||||
const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
|
||||
int ext_id, int *ext_index);
|
||||
|
||||
|
|
|
@ -50,6 +50,12 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
|
|||
int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1,
|
||||
const struct device_node *port2);
|
||||
int drm_of_lvds_get_data_mapping(const struct device_node *port);
|
||||
int drm_of_get_data_lanes_count(const struct device_node *endpoint,
|
||||
const unsigned int min, const unsigned int max);
|
||||
int drm_of_get_data_lanes_count_ep(const struct device_node *port,
|
||||
int port_reg, int reg,
|
||||
const unsigned int min,
|
||||
const unsigned int max);
|
||||
#else
|
||||
static inline uint32_t drm_of_crtc_port_mask(struct drm_device *dev,
|
||||
struct device_node *port)
|
||||
|
@ -105,6 +111,22 @@ drm_of_lvds_get_data_mapping(const struct device_node *port)
|
|||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drm_of_get_data_lanes_count(const struct device_node *endpoint,
|
||||
const unsigned int min, const unsigned int max)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drm_of_get_data_lanes_count_ep(const struct device_node *port,
|
||||
int port_reg, int reg,
|
||||
const unsigned int min,
|
||||
const unsigned int max)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
* memcpy(vaddr, src, len);
|
||||
*
|
||||
* void *vaddr_iomem = ...; // pointer to I/O memory
|
||||
* memcpy_toio(vaddr, _iomem, src, len);
|
||||
* memcpy_toio(vaddr_iomem, src, len);
|
||||
*
|
||||
* The user of such pointer may not have information about the mapping of that
|
||||
* region or may want to have a single code path to handle operations on that
|
||||
|
|
|
@ -55,6 +55,18 @@ struct efifb_dmi_info {
|
|||
int flags;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYSFB
|
||||
|
||||
void sysfb_disable(void);
|
||||
|
||||
#else /* CONFIG_SYSFB */
|
||||
|
||||
static inline void sysfb_disable(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SYSFB */
|
||||
|
||||
#ifdef CONFIG_EFI
|
||||
|
||||
extern struct efifb_dmi_info efifb_dmi_list[];
|
||||
|
@ -72,8 +84,8 @@ static inline void sysfb_apply_efi_quirks(struct platform_device *pd)
|
|||
|
||||
bool sysfb_parse_mode(const struct screen_info *si,
|
||||
struct simplefb_platform_data *mode);
|
||||
int sysfb_create_simplefb(const struct screen_info *si,
|
||||
const struct simplefb_platform_data *mode);
|
||||
struct platform_device *sysfb_create_simplefb(const struct screen_info *si,
|
||||
const struct simplefb_platform_data *mode);
|
||||
|
||||
#else /* CONFIG_SYSFB_SIMPLE */
|
||||
|
||||
|
@ -83,10 +95,10 @@ static inline bool sysfb_parse_mode(const struct screen_info *si,
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline int sysfb_create_simplefb(const struct screen_info *si,
|
||||
const struct simplefb_platform_data *mode)
|
||||
static inline struct platform_device *sysfb_create_simplefb(const struct screen_info *si,
|
||||
const struct simplefb_platform_data *mode)
|
||||
{
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SYSFB_SIMPLE */
|
||||
|
|
|
@ -85,6 +85,88 @@ struct dma_buf_sync {
|
|||
|
||||
#define DMA_BUF_NAME_LEN 32
|
||||
|
||||
/**
|
||||
* struct dma_buf_export_sync_file - Get a sync_file from a dma-buf
|
||||
*
|
||||
* Userspace can perform a DMA_BUF_IOCTL_EXPORT_SYNC_FILE to retrieve the
|
||||
* current set of fences on a dma-buf file descriptor as a sync_file. CPU
|
||||
* waits via poll() or other driver-specific mechanisms typically wait on
|
||||
* whatever fences are on the dma-buf at the time the wait begins. This
|
||||
* is similar except that it takes a snapshot of the current fences on the
|
||||
* dma-buf for waiting later instead of waiting immediately. This is
|
||||
* useful for modern graphics APIs such as Vulkan which assume an explicit
|
||||
* synchronization model but still need to inter-operate with dma-buf.
|
||||
*
|
||||
* The intended usage pattern is the following:
|
||||
*
|
||||
* 1. Export a sync_file with flags corresponding to the expected GPU usage
|
||||
* via DMA_BUF_IOCTL_EXPORT_SYNC_FILE.
|
||||
*
|
||||
* 2. Submit rendering work which uses the dma-buf. The work should wait on
|
||||
* the exported sync file before rendering and produce another sync_file
|
||||
* when complete.
|
||||
*
|
||||
* 3. Import the rendering-complete sync_file into the dma-buf with flags
|
||||
* corresponding to the GPU usage via DMA_BUF_IOCTL_IMPORT_SYNC_FILE.
|
||||
*
|
||||
* Unlike doing implicit synchronization via a GPU kernel driver's exec ioctl,
|
||||
* the above is not a single atomic operation. If userspace wants to ensure
|
||||
* ordering via these fences, it is the respnosibility of userspace to use
|
||||
* locks or other mechanisms to ensure that no other context adds fences or
|
||||
* submits work between steps 1 and 3 above.
|
||||
*/
|
||||
struct dma_buf_export_sync_file {
|
||||
/**
|
||||
* @flags: Read/write flags
|
||||
*
|
||||
* Must be DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, or both.
|
||||
*
|
||||
* If DMA_BUF_SYNC_READ is set and DMA_BUF_SYNC_WRITE is not set,
|
||||
* the returned sync file waits on any writers of the dma-buf to
|
||||
* complete. Waiting on the returned sync file is equivalent to
|
||||
* poll() with POLLIN.
|
||||
*
|
||||
* If DMA_BUF_SYNC_WRITE is set, the returned sync file waits on
|
||||
* any users of the dma-buf (read or write) to complete. Waiting
|
||||
* on the returned sync file is equivalent to poll() with POLLOUT.
|
||||
* If both DMA_BUF_SYNC_WRITE and DMA_BUF_SYNC_READ are set, this
|
||||
* is equivalent to just DMA_BUF_SYNC_WRITE.
|
||||
*/
|
||||
__u32 flags;
|
||||
/** @fd: Returned sync file descriptor */
|
||||
__s32 fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dma_buf_import_sync_file - Insert a sync_file into a dma-buf
|
||||
*
|
||||
* Userspace can perform a DMA_BUF_IOCTL_IMPORT_SYNC_FILE to insert a
|
||||
* sync_file into a dma-buf for the purposes of implicit synchronization
|
||||
* with other dma-buf consumers. This allows clients using explicitly
|
||||
* synchronized APIs such as Vulkan to inter-op with dma-buf consumers
|
||||
* which expect implicit synchronization such as OpenGL or most media
|
||||
* drivers/video.
|
||||
*/
|
||||
struct dma_buf_import_sync_file {
|
||||
/**
|
||||
* @flags: Read/write flags
|
||||
*
|
||||
* Must be DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, or both.
|
||||
*
|
||||
* If DMA_BUF_SYNC_READ is set and DMA_BUF_SYNC_WRITE is not set,
|
||||
* this inserts the sync_file as a read-only fence. Any subsequent
|
||||
* implicitly synchronized writes to this dma-buf will wait on this
|
||||
* fence but reads will not.
|
||||
*
|
||||
* If DMA_BUF_SYNC_WRITE is set, this inserts the sync_file as a
|
||||
* write fence. All subsequent implicitly synchronized access to
|
||||
* this dma-buf will wait on this fence.
|
||||
*/
|
||||
__u32 flags;
|
||||
/** @fd: Sync file descriptor */
|
||||
__s32 fd;
|
||||
};
|
||||
|
||||
#define DMA_BUF_BASE 'b'
|
||||
#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
|
||||
|
||||
|
@ -94,5 +176,7 @@ struct dma_buf_sync {
|
|||
#define DMA_BUF_SET_NAME _IOW(DMA_BUF_BASE, 1, const char *)
|
||||
#define DMA_BUF_SET_NAME_A _IOW(DMA_BUF_BASE, 1, __u32)
|
||||
#define DMA_BUF_SET_NAME_B _IOW(DMA_BUF_BASE, 1, __u64)
|
||||
#define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file)
|
||||
#define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file)
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue