drm-misc-next for v5.3:

UAPI Changes:
 - Give each dma-buf their own inode, add DMA_BUF_SET_NAME ioctl and a show_fdinfo handler.
 
 Cross-subsystem Changes:
 - Pull in the topic/remove-fbcon-notifiers branch:
   * remove fbdev notifier usage for fbcon, as prep work to clean up the fbcon locking
   * assorted locking checks in vt/console code
   * assorted notifier and cleanups in fbdev and backlight code
 
 Core Changes:
 - Make drm_debugfs_create_files() never fail.
 - add debug print to update_vblank_count.
 - Add DP_DPCD_QUIRK_NO_SINK_COUNT quirk.
 - Add todo item for drm_gem_objects.
 - Unexport drm_gem_(un)pin/v(un)map.
 - Document struct drm_cmdline_mode.
 - Rewrite the command handler for mode names, and add support to specify
   rotation, reflection and overscan. With a new selftest! :)
 - Fixes to drm/client for improving rotation support, and fixing variable scope.
 - Small fixes to self refresh helper.
 
 Driver Changes:
 - Add rockchip RK3328 support.
 - Assorted driver fixes to rockchip, vc4, rcar-du, vkms.
 - Expose panfrost performance counters through unstable ioctl's, hidden
   behind a module parameter.
 - Enumerate CRC sources list in vkms.
 - Add a basic kms driver for the Ingenic JZ47xx SoC, which will be expanded
   soon with more advanced features.
 - Suspend/resume fix for stm.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAl0LqJIACgkQ/lWMcqZw
 E8OWPA/8DryVJQE8ld6SB9nRhd8BAYLPH2f/mNN2yttqElku1rEWCvVHq6v4zrmZ
 kOkJUsutFC9YEwKphCbfb24xtRS/o5bAk05BQMMzu0wTj/hga2uS9zkOfmgKtVFJ
 u73J5VThWQKqjCMGOgrInd8tGPq0Kazzc3TrFhjAicM3fhmkz5vd64MvJifTpAM9
 nkG7cr53eUQfcKKv52rEY5DzV8NelnyASOMVdcsdqpcteJVhIcB0hXAULsLb3pGR
 dOADvzo8yIFERCCYnzBV85PO7vzTC+4zOFUZnfMHQ6THzvo3jzXkaOAyTV+KFv+M
 bJEJSPf9zRSqEehae9yekjMH0R819f1RnVQc7lteSMYiR/T+gciyZtf9wGoasuhd
 MoyhoOr3l2l4SGrGelrrKFIe3Ht1FUTA+Ni5cyJv0/FUms9sqMODTytXpSuUvxqg
 +4r95+Ly/06RxiUxfPZAtvamaQLr5PaiNdilytg0m4NzgJqcbx2xWeKo9bnt1QA4
 7doC/USc0bsjtf9kDSDTTT1T0d/KYtUmpMHCSy0OmjnI4wPh9RxOyoSKfIs7uVSB
 92q194KHTu0BmWeWS9XoIMs+vK7DiaeTX9PiHusbfhpapkoDFddG0JosrAfIyqKl
 cGs3ODdA6kM2/j4j4bsZHdULkVOPDrmxiz6qoq6mfPTBemxlcYA=
 =A67v
 -----END PGP SIGNATURE-----

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

drm-misc-next for v5.3:

UAPI Changes:
- Give each dma-buf their own inode, add DMA_BUF_SET_NAME ioctl and a show_fdinfo handler.

Cross-subsystem Changes:
- Pull in the topic/remove-fbcon-notifiers branch:
  * remove fbdev notifier usage for fbcon, as prep work to clean up the fbcon locking
  * assorted locking checks in vt/console code
  * assorted notifier and cleanups in fbdev and backlight code

Core Changes:
- Make drm_debugfs_create_files() never fail.
- add debug print to update_vblank_count.
- Add DP_DPCD_QUIRK_NO_SINK_COUNT quirk.
- Add todo item for drm_gem_objects.
- Unexport drm_gem_(un)pin/v(un)map.
- Document struct drm_cmdline_mode.
- Rewrite the command handler for mode names, and add support to specify
  rotation, reflection and overscan. With a new selftest! :)
- Fixes to drm/client for improving rotation support, and fixing variable scope.
- Small fixes to self refresh helper.

Driver Changes:
- Add rockchip RK3328 support.
- Assorted driver fixes to rockchip, vc4, rcar-du, vkms.
- Expose panfrost performance counters through unstable ioctl's, hidden
  behind a module parameter.
- Enumerate CRC sources list in vkms.
- Add a basic kms driver for the Ingenic JZ47xx SoC, which will be expanded
  soon with more advanced features.
- Suspend/resume fix for stm.

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

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/18e22ec1-adf3-3a75-34a3-9fe09a91eef5@linux.intel.com
This commit is contained in:
Dave Airlie 2019-06-21 13:54:46 +10:00
commit 39a207d0cf
82 changed files with 3564 additions and 1043 deletions

View File

@ -0,0 +1,44 @@
Ingenic JZ47xx LCD driver
Required properties:
- compatible: one of:
* ingenic,jz4740-lcd
* ingenic,jz4725b-lcd
- reg: LCD registers location and length
- clocks: LCD pixclock and device clock specifiers.
The device clock is only required on the JZ4740.
- clock-names: "lcd_pclk" and "lcd"
- interrupts: Specifies the interrupt line the LCD controller is connected to.
Example:
panel {
compatible = "sharp,ls020b1dd01d";
backlight = <&backlight>;
power-supply = <&vcc>;
port {
panel_input: endpoint {
remote-endpoint = <&panel_output>;
};
};
};
lcd: lcd-controller@13050000 {
compatible = "ingenic,jz4725b-lcd";
reg = <0x13050000 0x1000>;
interrupt-parent = <&intc>;
interrupts = <31>;
clocks = <&cgu JZ4725B_CLK_LCD>;
clock-names = "lcd";
port {
panel_output: endpoint {
remote-endpoint = <&panel_input>;
};
};
};

View File

@ -12,6 +12,7 @@ following device-specific properties.
Required properties:
- compatible: should be one of the following:
"rockchip,rk3228-dw-hdmi"
"rockchip,rk3288-dw-hdmi"
"rockchip,rk3328-dw-hdmi"
"rockchip,rk3399-dw-hdmi"

View File

@ -51,6 +51,20 @@ To force the VGA output to be enabled and drive a specific mode say:
Specifying the option multiple times for different ports is possible, e.g.:
video=LVDS-1:d video=HDMI-1:D
Options can also be passed after the mode, using commas as separator.
Sample usage: 720x480,rotate=180 - 720x480 mode, rotated by 180 degrees
Valid options are:
- margin_top, margin_bottom, margin_left, margin_right (integer):
Number of pixels in the margins, typically to deal with overscan on TVs
- reflect_x (boolean): Perform an axial symmetry on the X axis
- reflect_y (boolean): Perform an axial symmetry on the Y axis
- rotate (integer): Rotate the initial framebuffer by x
degrees. Valid values are 0, 90, 180 and 270.
***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo *****
What is the VESA(TM) Coordinated Video Timings (CVT)?

View File

@ -228,6 +228,12 @@ struct drm_gem_object_funcs
GEM objects can now have a function table instead of having the callbacks on the
DRM driver struct. This is now the preferred way and drivers can be moved over.
DRM_GEM_CMA_VMAP_DRIVER_OPS, DRM_GEM_SHMEM_DRIVER_OPS already support this, but
DRM_GEM_VRAM_DRIVER_PRIME does not yet and needs to be aligned with the previous
two. We also need a 2nd version of the CMA define that doesn't require the
vmapping to be present (different hook for prime importing). Plus this needs to
be rolled out to all drivers using their own implementations, too.
Use DRM_MODESET_LOCK_ALL_* helpers instead of boilerplate
---------------------------------------------------------

View File

@ -347,8 +347,17 @@ int __init am200_init(void)
{
int ret;
/* before anything else, we request notification for any fb
* creation events */
/*
* Before anything else, we request notification for any fb
* creation events.
*
* FIXME: This is terrible and needs to be nuked. The notifier is used
* to get at the fb base address from the boot splash fb driver, which
* is then passed to metronomefb. Instaed of metronomfb or this board
* support file here figuring this out on their own.
*
* See also the #ifdef in fbmem.c.
*/
fb_register_client(&am200_fb_notif);
pxa2xx_mfp_config(ARRAY_AND_SIZE(am200_pin_config));

View File

@ -34,8 +34,10 @@
#include <linux/poll.h>
#include <linux/reservation.h>
#include <linux/mm.h>
#include <linux/mount.h>
#include <uapi/linux/dma-buf.h>
#include <uapi/linux/magic.h>
static inline int is_dma_buf_file(struct file *);
@ -46,6 +48,41 @@ struct dma_buf_list {
static struct dma_buf_list db_list;
static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
{
struct dma_buf *dmabuf;
char name[DMA_BUF_NAME_LEN];
size_t ret = 0;
dmabuf = dentry->d_fsdata;
mutex_lock(&dmabuf->lock);
if (dmabuf->name)
ret = strlcpy(name, dmabuf->name, DMA_BUF_NAME_LEN);
mutex_unlock(&dmabuf->lock);
return dynamic_dname(dentry, buffer, buflen, "/%s:%s",
dentry->d_name.name, ret > 0 ? name : "");
}
static const struct dentry_operations dma_buf_dentry_ops = {
.d_dname = dmabuffs_dname,
};
static struct vfsmount *dma_buf_mnt;
static struct dentry *dma_buf_fs_mount(struct file_system_type *fs_type,
int flags, const char *name, void *data)
{
return mount_pseudo(fs_type, "dmabuf:", NULL, &dma_buf_dentry_ops,
DMA_BUF_MAGIC);
}
static struct file_system_type dma_buf_fs_type = {
.name = "dmabuf",
.mount = dma_buf_fs_mount,
.kill_sb = kill_anon_super,
};
static int dma_buf_release(struct inode *inode, struct file *file)
{
struct dma_buf *dmabuf;
@ -280,6 +317,43 @@ out:
return events;
}
/**
* dma_buf_set_name - Set a name to a specific dma_buf to track the usage.
* The name of the dma-buf buffer can only be set when the dma-buf is not
* attached to any devices. It could theoritically support changing the
* name of the dma-buf if the same piece of memory is used for multiple
* purpose between different devices.
*
* @dmabuf [in] dmabuf buffer that will be renamed.
* @buf: [in] A piece of userspace memory that contains the name of
* the dma-buf.
*
* Returns 0 on success. If the dma-buf buffer is already attached to
* devices, return -EBUSY.
*
*/
static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf)
{
char *name = strndup_user(buf, DMA_BUF_NAME_LEN);
long ret = 0;
if (IS_ERR(name))
return PTR_ERR(name);
mutex_lock(&dmabuf->lock);
if (!list_empty(&dmabuf->attachments)) {
ret = -EBUSY;
kfree(name);
goto out_unlock;
}
kfree(dmabuf->name);
dmabuf->name = name;
out_unlock:
mutex_unlock(&dmabuf->lock);
return ret;
}
static long dma_buf_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
@ -318,11 +392,29 @@ static long dma_buf_ioctl(struct file *file,
ret = dma_buf_begin_cpu_access(dmabuf, direction);
return ret;
case DMA_BUF_SET_NAME:
return dma_buf_set_name(dmabuf, (const char __user *)arg);
default:
return -ENOTTY;
}
}
static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file)
{
struct dma_buf *dmabuf = file->private_data;
seq_printf(m, "size:\t%zu\n", dmabuf->size);
/* Don't count the temporary reference taken inside procfs seq_show */
seq_printf(m, "count:\t%ld\n", file_count(dmabuf->file) - 1);
seq_printf(m, "exp_name:\t%s\n", dmabuf->exp_name);
mutex_lock(&dmabuf->lock);
if (dmabuf->name)
seq_printf(m, "name:\t%s\n", dmabuf->name);
mutex_unlock(&dmabuf->lock);
}
static const struct file_operations dma_buf_fops = {
.release = dma_buf_release,
.mmap = dma_buf_mmap_internal,
@ -332,6 +424,7 @@ static const struct file_operations dma_buf_fops = {
#ifdef CONFIG_COMPAT
.compat_ioctl = dma_buf_ioctl,
#endif
.show_fdinfo = dma_buf_show_fdinfo,
};
/*
@ -342,6 +435,32 @@ static inline int is_dma_buf_file(struct file *file)
return file->f_op == &dma_buf_fops;
}
static struct file *dma_buf_getfile(struct dma_buf *dmabuf, int flags)
{
struct file *file;
struct inode *inode = alloc_anon_inode(dma_buf_mnt->mnt_sb);
if (IS_ERR(inode))
return ERR_CAST(inode);
inode->i_size = dmabuf->size;
inode_set_bytes(inode, dmabuf->size);
file = alloc_file_pseudo(inode, dma_buf_mnt, "dmabuf",
flags, &dma_buf_fops);
if (IS_ERR(file))
goto err_alloc_file;
file->f_flags = flags & (O_ACCMODE | O_NONBLOCK);
file->private_data = dmabuf;
file->f_path.dentry->d_fsdata = dmabuf;
return file;
err_alloc_file:
iput(inode);
return file;
}
/**
* DOC: dma buf device access
*
@ -436,8 +555,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
}
dmabuf->resv = resv;
file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf,
exp_info->flags);
file = dma_buf_getfile(dmabuf, exp_info->flags);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto err_dmabuf;
@ -1055,8 +1173,8 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
return ret;
seq_puts(s, "\nDma-buf Objects:\n");
seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\n",
"size", "flags", "mode", "count");
seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\n",
"size", "flags", "mode", "count", "ino");
list_for_each_entry(buf_obj, &db_list.head, list_node) {
ret = mutex_lock_interruptible(&buf_obj->lock);
@ -1067,11 +1185,13 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
continue;
}
seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\n",
seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\t%08lu\t%s\n",
buf_obj->size,
buf_obj->file->f_flags, buf_obj->file->f_mode,
file_count(buf_obj->file),
buf_obj->exp_name);
buf_obj->exp_name,
file_inode(buf_obj->file)->i_ino,
buf_obj->name ?: "");
robj = buf_obj->resv;
while (true) {
@ -1167,6 +1287,10 @@ static inline void dma_buf_uninit_debugfs(void)
static int __init dma_buf_init(void)
{
dma_buf_mnt = kern_mount(&dma_buf_fs_type);
if (IS_ERR(dma_buf_mnt))
return PTR_ERR(dma_buf_mnt);
mutex_init(&db_list.lock);
INIT_LIST_HEAD(&db_list.head);
dma_buf_init_debugfs();
@ -1177,5 +1301,6 @@ subsys_initcall(dma_buf_init);
static void __exit dma_buf_deinit(void)
{
dma_buf_uninit_debugfs();
kern_unmount(dma_buf_mnt);
}
__exitcall(dma_buf_deinit);

View File

@ -316,6 +316,8 @@ source "drivers/gpu/drm/sti/Kconfig"
source "drivers/gpu/drm/imx/Kconfig"
source "drivers/gpu/drm/ingenic/Kconfig"
source "drivers/gpu/drm/v3d/Kconfig"
source "drivers/gpu/drm/vc4/Kconfig"

View File

@ -99,6 +99,7 @@ obj-$(CONFIG_DRM_TEGRA) += tegra/
obj-$(CONFIG_DRM_STM) += stm/
obj-$(CONFIG_DRM_STI) += sti/
obj-$(CONFIG_DRM_IMX) += imx/
obj-$(CONFIG_DRM_INGENIC) += ingenic/
obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
obj-$(CONFIG_DRM_MESON) += meson/
obj-y += i2c/

View File

@ -379,6 +379,24 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
/**
* drm_atomic_helper_connector_tv_reset - Resets TV connector properties
* @connector: DRM connector
*
* Resets the TV-related properties attached to a connector.
*/
void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector)
{
struct drm_cmdline_mode *cmdline = &connector->cmdline_mode;
struct drm_connector_state *state = connector->state;
state->tv.margins.left = cmdline->tv_margins.left;
state->tv.margins.right = cmdline->tv_margins.right;
state->tv.margins.top = cmdline->tv_margins.top;
state->tv.margins.bottom = cmdline->tv_margins.bottom;
}
EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset);
/**
* __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
* @connector: connector object

View File

@ -149,6 +149,10 @@ drm_connector_pick_cmdline_mode(struct drm_connector *connector)
prefer_non_interlace = !cmdline_mode->interlace;
again:
list_for_each_entry(mode, &connector->modes, head) {
/* Check (optional) mode name first */
if (!strcmp(mode->name, cmdline_mode->name))
return mode;
/* check width/height */
if (mode->hdisplay != cmdline_mode->xres ||
mode->vdisplay != cmdline_mode->yres)
@ -804,22 +808,23 @@ free_connectors:
EXPORT_SYMBOL(drm_client_modeset_probe);
/**
* drm_client_panel_rotation() - Check panel orientation
* drm_client_rotation() - Check the initial rotation value
* @modeset: DRM modeset
* @rotation: Returned rotation value
*
* This function checks if the primary plane in @modeset can hw rotate to match
* the panel orientation on its connector.
* This function checks if the primary plane in @modeset can hw rotate
* to match the rotation needed on its connector.
*
* Note: Currently only 0 and 180 degrees are supported.
*
* Return:
* True if the plane can do the rotation, false otherwise.
*/
bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation)
bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation)
{
struct drm_connector *connector = modeset->connectors[0];
struct drm_plane *plane = modeset->crtc->primary;
struct drm_cmdline_mode *cmdline;
u64 valid_mask = 0;
unsigned int i;
@ -840,12 +845,42 @@ bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotat
*rotation = DRM_MODE_ROTATE_0;
}
/**
* The panel already defined the default rotation
* through its orientation. Whatever has been provided
* on the command line needs to be added to that.
*
* Unfortunately, the rotations are at different bit
* indices, so the math to add them up are not as
* trivial as they could.
*
* Reflections on the other hand are pretty trivial to deal with, a
* simple XOR between the two handle the addition nicely.
*/
cmdline = &connector->cmdline_mode;
if (cmdline->specified) {
unsigned int cmdline_rest, panel_rest;
unsigned int cmdline_rot, panel_rot;
unsigned int sum_rot, sum_rest;
panel_rot = ilog2(*rotation & DRM_MODE_ROTATE_MASK);
cmdline_rot = ilog2(cmdline->rotation_reflection & DRM_MODE_ROTATE_MASK);
sum_rot = (panel_rot + cmdline_rot) % 4;
panel_rest = *rotation & ~DRM_MODE_ROTATE_MASK;
cmdline_rest = cmdline->rotation_reflection & ~DRM_MODE_ROTATE_MASK;
sum_rest = panel_rest ^ cmdline_rest;
*rotation = (1 << sum_rot) | sum_rest;
}
/*
* TODO: support 90 / 270 degree hardware rotation,
* depending on the hardware this may require the framebuffer
* to be in a specific tiling format.
*/
if (*rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property)
if ((*rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_180 ||
!plane->rotation_property)
return false;
for (i = 0; i < plane->rotation_property->num_values; i++)
@ -856,12 +891,11 @@ bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotat
return true;
}
EXPORT_SYMBOL(drm_client_panel_rotation);
EXPORT_SYMBOL(drm_client_rotation);
static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool active)
{
struct drm_device *dev = client->dev;
struct drm_plane_state *plane_state;
struct drm_plane *plane;
struct drm_atomic_state *state;
struct drm_modeset_acquire_ctx ctx;
@ -879,6 +913,8 @@ static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool
state->acquire_ctx = &ctx;
retry:
drm_for_each_plane(plane, dev) {
struct drm_plane_state *plane_state;
plane_state = drm_atomic_get_plane_state(state, plane);
if (IS_ERR(plane_state)) {
ret = PTR_ERR(plane_state);
@ -900,7 +936,9 @@ retry:
struct drm_plane *primary = mode_set->crtc->primary;
unsigned int rotation;
if (drm_client_panel_rotation(mode_set, &rotation)) {
if (drm_client_rotation(mode_set, &rotation)) {
struct drm_plane_state *plane_state;
/* Cannot fail as we've already gotten the plane state above */
plane_state = drm_atomic_get_new_plane_state(state, primary);
plane_state->rotation = rotation;

View File

@ -139,8 +139,9 @@ static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
connector->force = mode->force;
}
DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
DRM_DEBUG_KMS("cmdline mode for connector %s %s %dx%d@%dHz%s%s%s\n",
connector->name,
mode->name ? mode->name : "",
mode->xres, mode->yres,
mode->refresh_specified ? mode->refresh : 60,
mode->rb ? " reduced blanking" : "",

View File

@ -176,9 +176,8 @@ int drm_debugfs_create_files(const struct drm_info_list *files, int count,
struct dentry *root, struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
struct dentry *ent;
struct drm_info_node *tmp;
int i, ret;
int i;
for (i = 0; i < count; i++) {
u32 features = files[i].driver_features;
@ -188,22 +187,13 @@ int drm_debugfs_create_files(const struct drm_info_list *files, int count,
continue;
tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
if (tmp == NULL) {
ret = -1;
goto fail;
}
ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO,
root, tmp, &drm_debugfs_fops);
if (!ent) {
DRM_ERROR("Cannot create /sys/kernel/debug/dri/%pd/%s\n",
root, files[i].name);
kfree(tmp);
ret = -1;
goto fail;
}
if (tmp == NULL)
continue;
tmp->minor = minor;
tmp->dent = ent;
tmp->dent = debugfs_create_file(files[i].name,
S_IFREG | S_IRUGO, root, tmp,
&drm_debugfs_fops);
tmp->info_ent = &files[i];
mutex_lock(&minor->debugfs_lock);
@ -211,10 +201,6 @@ int drm_debugfs_create_files(const struct drm_info_list *files, int count,
mutex_unlock(&minor->debugfs_lock);
}
return 0;
fail:
drm_debugfs_remove_files(files, count, minor);
return ret;
}
EXPORT_SYMBOL(drm_debugfs_create_files);

View File

@ -1280,7 +1280,9 @@ static const struct dpcd_quirk dpcd_quirk_list[] = {
/* LG LP140WF6-SPM1 eDP panel */
{ OUI(0x00, 0x22, 0xb9), DEVICE_ID('s', 'i', 'v', 'a', 'r', 'T'), false, BIT(DP_DPCD_QUIRK_CONSTANT_N) },
/* Apple panels need some additional handling to support PSR */
{ OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) }
{ OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) },
/* CH7511 seems to leave SINK_COUNT zeroed */
{ OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) },
};
#undef OUI

View File

@ -1722,7 +1722,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
modeset->fb = fb_helper->fb;
if (drm_client_panel_rotation(modeset, &rotation))
if (drm_client_rotation(modeset, &rotation))
/* Rotating in hardware, fbcon should not rotate */
sw_rotations |= DRM_MODE_ROTATE_0;
else

View File

@ -1216,15 +1216,6 @@ void drm_gem_print_info(struct drm_printer *p, unsigned int indent,
obj->dev->driver->gem_print_info(p, indent, obj);
}
/**
* drm_gem_pin - Pin backing buffer in memory
* @obj: GEM object
*
* Make sure the backing buffer is pinned in memory.
*
* Returns:
* 0 on success or a negative error code on failure.
*/
int drm_gem_pin(struct drm_gem_object *obj)
{
if (obj->funcs && obj->funcs->pin)
@ -1234,14 +1225,7 @@ int drm_gem_pin(struct drm_gem_object *obj)
else
return 0;
}
EXPORT_SYMBOL(drm_gem_pin);
/**
* drm_gem_unpin - Unpin backing buffer from memory
* @obj: GEM object
*
* Relax the requirement that the backing buffer is pinned in memory.
*/
void drm_gem_unpin(struct drm_gem_object *obj)
{
if (obj->funcs && obj->funcs->unpin)
@ -1249,16 +1233,7 @@ void drm_gem_unpin(struct drm_gem_object *obj)
else if (obj->dev->driver->gem_prime_unpin)
obj->dev->driver->gem_prime_unpin(obj);
}
EXPORT_SYMBOL(drm_gem_unpin);
/**
* drm_gem_vmap - Map buffer into kernel virtual address space
* @obj: GEM object
*
* Returns:
* A virtual pointer to a newly created GEM object or an ERR_PTR-encoded negative
* error code on failure.
*/
void *drm_gem_vmap(struct drm_gem_object *obj)
{
void *vaddr;
@ -1275,13 +1250,7 @@ void *drm_gem_vmap(struct drm_gem_object *obj)
return vaddr;
}
EXPORT_SYMBOL(drm_gem_vmap);
/**
* drm_gem_vunmap - Remove buffer mapping from kernel virtual address space
* @obj: GEM object
* @vaddr: Virtual address (can be NULL)
*/
void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr)
{
if (!vaddr)
@ -1292,7 +1261,6 @@ void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr)
else if (obj->dev->driver->gem_prime_vunmap)
obj->dev->driver->gem_prime_vunmap(obj, vaddr);
}
EXPORT_SYMBOL(drm_gem_vunmap);
/**
* drm_gem_lock_reservations - Sets up the ww context and acquires

View File

@ -133,6 +133,11 @@ void drm_gem_release(struct drm_device *dev, struct drm_file *file_private);
void drm_gem_print_info(struct drm_printer *p, unsigned int indent,
const struct drm_gem_object *obj);
int drm_gem_pin(struct drm_gem_object *obj);
void drm_gem_unpin(struct drm_gem_object *obj);
void *drm_gem_vmap(struct drm_gem_object *obj);
void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr);
/* drm_debugfs.c drm_debugfs_crc.c */
#if defined(CONFIG_DEBUG_FS)
int drm_debugfs_init(struct drm_minor *minor, int minor_id,

View File

@ -30,6 +30,7 @@
* authorization from the copyright holder(s) and author(s).
*/
#include <linux/ctype.h>
#include <linux/list.h>
#include <linux/list_sort.h>
#include <linux/export.h>
@ -1408,6 +1409,260 @@ void drm_connector_list_update(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_connector_list_update);
static int drm_mode_parse_cmdline_bpp(const char *str, char **end_ptr,
struct drm_cmdline_mode *mode)
{
unsigned int bpp;
if (str[0] != '-')
return -EINVAL;
str++;
bpp = simple_strtol(str, end_ptr, 10);
if (*end_ptr == str)
return -EINVAL;
mode->bpp = bpp;
mode->bpp_specified = true;
return 0;
}
static int drm_mode_parse_cmdline_refresh(const char *str, char **end_ptr,
struct drm_cmdline_mode *mode)
{
unsigned int refresh;
if (str[0] != '@')
return -EINVAL;
str++;
refresh = simple_strtol(str, end_ptr, 10);
if (*end_ptr == str)
return -EINVAL;
mode->refresh = refresh;
mode->refresh_specified = true;
return 0;
}
static int drm_mode_parse_cmdline_extra(const char *str, int length,
struct drm_connector *connector,
struct drm_cmdline_mode *mode)
{
int i;
for (i = 0; i < length; i++) {
switch (str[i]) {
case 'i':
mode->interlace = true;
break;
case 'm':
mode->margins = true;
break;
case 'D':
if (mode->force != DRM_FORCE_UNSPECIFIED)
return -EINVAL;
if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
(connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
mode->force = DRM_FORCE_ON;
else
mode->force = DRM_FORCE_ON_DIGITAL;
break;
case 'd':
if (mode->force != DRM_FORCE_UNSPECIFIED)
return -EINVAL;
mode->force = DRM_FORCE_OFF;
break;
case 'e':
if (mode->force != DRM_FORCE_UNSPECIFIED)
return -EINVAL;
mode->force = DRM_FORCE_ON;
break;
default:
return -EINVAL;
}
}
return 0;
}
static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length,
bool extras,
struct drm_connector *connector,
struct drm_cmdline_mode *mode)
{
const char *str_start = str;
bool rb = false, cvt = false;
int xres = 0, yres = 0;
int remaining, i;
char *end_ptr;
xres = simple_strtol(str, &end_ptr, 10);
if (end_ptr == str)
return -EINVAL;
if (end_ptr[0] != 'x')
return -EINVAL;
end_ptr++;
str = end_ptr;
yres = simple_strtol(str, &end_ptr, 10);
if (end_ptr == str)
return -EINVAL;
remaining = length - (end_ptr - str_start);
if (remaining < 0)
return -EINVAL;
for (i = 0; i < remaining; i++) {
switch (end_ptr[i]) {
case 'M':
cvt = true;
break;
case 'R':
rb = true;
break;
default:
/*
* Try to pass that to our extras parsing
* function to handle the case where the
* extras are directly after the resolution
*/
if (extras) {
int ret = drm_mode_parse_cmdline_extra(end_ptr + i,
1,
connector,
mode);
if (ret)
return ret;
} else {
return -EINVAL;
}
}
}
mode->xres = xres;
mode->yres = yres;
mode->cvt = cvt;
mode->rb = rb;
return 0;
}
static int drm_mode_parse_cmdline_options(char *str, size_t len,
struct drm_connector *connector,
struct drm_cmdline_mode *mode)
{
unsigned int rotation = 0;
char *sep = str;
while ((sep = strchr(sep, ','))) {
char *delim, *option;
option = sep + 1;
delim = strchr(option, '=');
if (!delim) {
delim = strchr(option, ',');
if (!delim)
delim = str + len;
}
if (!strncmp(option, "rotate", delim - option)) {
const char *value = delim + 1;
unsigned int deg;
deg = simple_strtol(value, &sep, 10);
/* Make sure we have parsed something */
if (sep == value)
return -EINVAL;
switch (deg) {
case 0:
rotation |= DRM_MODE_ROTATE_0;
break;
case 90:
rotation |= DRM_MODE_ROTATE_90;
break;
case 180:
rotation |= DRM_MODE_ROTATE_180;
break;
case 270:
rotation |= DRM_MODE_ROTATE_270;
break;
default:
return -EINVAL;
}
} else if (!strncmp(option, "reflect_x", delim - option)) {
rotation |= DRM_MODE_REFLECT_X;
sep = delim;
} else if (!strncmp(option, "reflect_y", delim - option)) {
rotation |= DRM_MODE_REFLECT_Y;
sep = delim;
} else if (!strncmp(option, "margin_right", delim - option)) {
const char *value = delim + 1;
unsigned int margin;
margin = simple_strtol(value, &sep, 10);
/* Make sure we have parsed something */
if (sep == value)
return -EINVAL;
mode->tv_margins.right = margin;
} else if (!strncmp(option, "margin_left", delim - option)) {
const char *value = delim + 1;
unsigned int margin;
margin = simple_strtol(value, &sep, 10);
/* Make sure we have parsed something */
if (sep == value)
return -EINVAL;
mode->tv_margins.left = margin;
} else if (!strncmp(option, "margin_top", delim - option)) {
const char *value = delim + 1;
unsigned int margin;
margin = simple_strtol(value, &sep, 10);
/* Make sure we have parsed something */
if (sep == value)
return -EINVAL;
mode->tv_margins.top = margin;
} else if (!strncmp(option, "margin_bottom", delim - option)) {
const char *value = delim + 1;
unsigned int margin;
margin = simple_strtol(value, &sep, 10);
/* Make sure we have parsed something */
if (sep == value)
return -EINVAL;
mode->tv_margins.bottom = margin;
} else {
return -EINVAL;
}
}
mode->rotation_reflection = rotation;
return 0;
}
/**
* drm_mode_parse_command_line_for_connector - parse command line modeline for connector
* @mode_option: optional per connector mode option
@ -1423,6 +1678,10 @@ EXPORT_SYMBOL(drm_connector_list_update);
*
* <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
*
* Additionals options can be provided following the mode, using a comma to
* separate each option. Valid options can be found in
* Documentation/fb/modedb.txt.
*
* The intermediate drm_cmdline_mode structure is required to store additional
* options from the command line modline like the force-enable/disable flag.
*
@ -1434,13 +1693,13 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
struct drm_cmdline_mode *mode)
{
const char *name;
unsigned int namelen;
bool res_specified = false, bpp_specified = false, refresh_specified = false;
unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
bool yres_specified = false, cvt = false, rb = false;
bool interlace = false, margins = false, was_digit = false;
int i;
enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
bool named_mode = false, parse_extras = false;
unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
unsigned int mode_end = 0;
char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
char *options_ptr = NULL;
char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
int ret;
#ifdef CONFIG_FB
if (!mode_option)
@ -1453,127 +1712,111 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
}
name = mode_option;
namelen = strlen(name);
for (i = namelen-1; i >= 0; i--) {
switch (name[i]) {
case '@':
if (!refresh_specified && !bpp_specified &&
!yres_specified && !cvt && !rb && was_digit) {
refresh = simple_strtol(&name[i+1], NULL, 10);
refresh_specified = true;
was_digit = false;
} else
goto done;
break;
case '-':
if (!bpp_specified && !yres_specified && !cvt &&
!rb && was_digit) {
bpp = simple_strtol(&name[i+1], NULL, 10);
bpp_specified = true;
was_digit = false;
} else
goto done;
break;
case 'x':
if (!yres_specified && was_digit) {
yres = simple_strtol(&name[i+1], NULL, 10);
yres_specified = true;
was_digit = false;
} else
goto done;
break;
case '0' ... '9':
was_digit = true;
break;
case 'M':
if (yres_specified || cvt || was_digit)
goto done;
cvt = true;
break;
case 'R':
if (yres_specified || cvt || rb || was_digit)
goto done;
rb = true;
break;
case 'm':
if (cvt || yres_specified || was_digit)
goto done;
margins = true;
break;
case 'i':
if (cvt || yres_specified || was_digit)
goto done;
interlace = true;
break;
case 'e':
if (yres_specified || bpp_specified || refresh_specified ||
was_digit || (force != DRM_FORCE_UNSPECIFIED))
goto done;
force = DRM_FORCE_ON;
break;
case 'D':
if (yres_specified || bpp_specified || refresh_specified ||
was_digit || (force != DRM_FORCE_UNSPECIFIED))
goto done;
/*
* This is a bit convoluted. To differentiate between the
* named modes and poorly formatted resolutions, we need a
* bunch of things:
* - We need to make sure that the first character (which
* would be our resolution in X) is a digit.
* - However, if the X resolution is missing, then we end up
* with something like x<yres>, with our first character
* being an alpha-numerical character, which would be
* considered a named mode.
*
* If this isn't enough, we should add more heuristics here,
* and matching unit-tests.
*/
if (!isdigit(name[0]) && name[0] != 'x')
named_mode = true;
if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
(connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
force = DRM_FORCE_ON;
else
force = DRM_FORCE_ON_DIGITAL;
break;
case 'd':
if (yres_specified || bpp_specified || refresh_specified ||
was_digit || (force != DRM_FORCE_UNSPECIFIED))
goto done;
force = DRM_FORCE_OFF;
break;
default:
goto done;
}
}
if (i < 0 && yres_specified) {
char *ch;
xres = simple_strtol(name, &ch, 10);
if ((ch != NULL) && (*ch == 'x'))
res_specified = true;
else
i = ch - name;
} else if (!yres_specified && was_digit) {
/* catch mode that begins with digits but has no 'x' */
i = 0;
}
done:
if (i >= 0) {
pr_warn("[drm] parse error at position %i in video mode '%s'\n",
i, name);
mode->specified = false;
return false;
}
if (res_specified) {
mode->specified = true;
mode->xres = xres;
mode->yres = yres;
}
if (refresh_specified) {
mode->refresh_specified = true;
mode->refresh = refresh;
}
if (bpp_specified) {
/* Try to locate the bpp and refresh specifiers, if any */
bpp_ptr = strchr(name, '-');
if (bpp_ptr) {
bpp_off = bpp_ptr - name;
mode->bpp_specified = true;
mode->bpp = bpp;
}
mode->rb = rb;
mode->cvt = cvt;
mode->interlace = interlace;
mode->margins = margins;
mode->force = force;
refresh_ptr = strchr(name, '@');
if (refresh_ptr) {
if (named_mode)
return false;
refresh_off = refresh_ptr - name;
mode->refresh_specified = true;
}
/* Locate the start of named options */
options_ptr = strchr(name, ',');
if (options_ptr)
options_off = options_ptr - name;
/* Locate the end of the name / resolution, and parse it */
if (bpp_ptr) {
mode_end = bpp_off;
} else if (refresh_ptr) {
mode_end = refresh_off;
} else if (options_ptr) {
mode_end = options_off;
} else {
mode_end = strlen(name);
parse_extras = true;
}
if (named_mode) {
strncpy(mode->name, name, mode_end);
} else {
ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
parse_extras,
connector,
mode);
if (ret)
return false;
}
mode->specified = true;
if (bpp_ptr) {
ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
if (ret)
return false;
}
if (refresh_ptr) {
ret = drm_mode_parse_cmdline_refresh(refresh_ptr,
&refresh_end_ptr, mode);
if (ret)
return false;
}
/*
* Locate the end of the bpp / refresh, and parse the extras
* if relevant
*/
if (bpp_ptr && refresh_ptr)
extra_ptr = max(bpp_end_ptr, refresh_end_ptr);
else if (bpp_ptr)
extra_ptr = bpp_end_ptr;
else if (refresh_ptr)
extra_ptr = refresh_end_ptr;
if (extra_ptr &&
extra_ptr != options_ptr) {
int len = strlen(name) - (extra_ptr - name);
ret = drm_mode_parse_cmdline_extra(extra_ptr, len,
connector, mode);
if (ret)
return false;
}
if (options_ptr) {
int len = strlen(name) - (options_ptr - name);
ret = drm_mode_parse_cmdline_options(options_ptr, len,
connector, mode);
if (ret)
return false;
}
return true;
}

View File

@ -69,14 +69,14 @@ static void drm_self_refresh_helper_entry_work(struct work_struct *work)
struct drm_connector *conn;
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
int i, ret;
int i, ret = 0;
drm_modeset_acquire_init(&ctx, 0);
state = drm_atomic_state_alloc(dev);
if (!state) {
ret = -ENOMEM;
goto out;
goto out_drop_locks;
}
retry:
@ -116,6 +116,8 @@ out:
}
drm_atomic_state_put(state);
out_drop_locks:
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
@ -205,7 +207,7 @@ void drm_self_refresh_helper_cleanup(struct drm_crtc *crtc)
struct drm_self_refresh_data *sr_data = crtc->self_refresh_data;
/* Helper is already uninitialized */
if (sr_data)
if (!sr_data)
return;
crtc->self_refresh_data = NULL;

View File

@ -241,12 +241,16 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
* on the difference in the timestamps and the
* frame/field duration.
*/
DRM_DEBUG_VBL("crtc %u: Calculating number of vblanks."
" diff_ns = %lld, framedur_ns = %d)\n",
pipe, (long long) diff_ns, framedur_ns);
diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
if (diff == 0 && in_vblank_irq)
DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
" diff_ns = %lld, framedur_ns = %d)\n",
pipe, (long long) diff_ns, framedur_ns);
DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored\n",
pipe);
} else {
/* some kind of default for drivers w/o accurate vbl timestamping */
diff = in_vblank_irq ? 1 : 0;

View File

@ -74,7 +74,8 @@ static pgprot_t drm_io_prot(struct drm_local_map *map,
/* We don't want graphics memory to be mapped encrypted */
tmp = pgprot_decrypted(tmp);
#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__)
#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || \
defined(__mips__)
if (map->type == _DRM_REGISTERS && !(map->flags & _DRM_WRITE_COMBINING))
tmp = pgprot_noncached(tmp);
else
@ -85,7 +86,7 @@ static pgprot_t drm_io_prot(struct drm_local_map *map,
tmp = pgprot_writecombine(tmp);
else
tmp = pgprot_noncached(tmp);
#elif defined(__sparc__) || defined(__arm__) || defined(__mips__)
#elif defined(__sparc__) || defined(__arm__)
tmp = pgprot_noncached(tmp);
#endif
return tmp;

View File

@ -0,0 +1,16 @@
config DRM_INGENIC
tristate "DRM Support for Ingenic SoCs"
depends on MIPS || COMPILE_TEST
depends on DRM
depends on CMA
depends on OF
select DRM_BRIDGE
select DRM_PANEL_BRIDGE
select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER
select DRM_GEM_CMA_HELPER
select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
help
Choose this option for DRM support for the Ingenic SoCs.
If M is selected the module will be called ingenic-drm.

View File

@ -0,0 +1 @@
obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o

View File

@ -0,0 +1,818 @@
// SPDX-License-Identifier: GPL-2.0
//
// Ingenic JZ47xx KMS driver
//
// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_irq.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_plane.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#define JZ_REG_LCD_CFG 0x00
#define JZ_REG_LCD_VSYNC 0x04
#define JZ_REG_LCD_HSYNC 0x08
#define JZ_REG_LCD_VAT 0x0C
#define JZ_REG_LCD_DAH 0x10
#define JZ_REG_LCD_DAV 0x14
#define JZ_REG_LCD_PS 0x18
#define JZ_REG_LCD_CLS 0x1C
#define JZ_REG_LCD_SPL 0x20
#define JZ_REG_LCD_REV 0x24
#define JZ_REG_LCD_CTRL 0x30
#define JZ_REG_LCD_STATE 0x34
#define JZ_REG_LCD_IID 0x38
#define JZ_REG_LCD_DA0 0x40
#define JZ_REG_LCD_SA0 0x44
#define JZ_REG_LCD_FID0 0x48
#define JZ_REG_LCD_CMD0 0x4C
#define JZ_REG_LCD_DA1 0x50
#define JZ_REG_LCD_SA1 0x54
#define JZ_REG_LCD_FID1 0x58
#define JZ_REG_LCD_CMD1 0x5C
#define JZ_LCD_CFG_SLCD BIT(31)
#define JZ_LCD_CFG_PS_DISABLE BIT(23)
#define JZ_LCD_CFG_CLS_DISABLE BIT(22)
#define JZ_LCD_CFG_SPL_DISABLE BIT(21)
#define JZ_LCD_CFG_REV_DISABLE BIT(20)
#define JZ_LCD_CFG_HSYNCM BIT(19)
#define JZ_LCD_CFG_PCLKM BIT(18)
#define JZ_LCD_CFG_INV BIT(17)
#define JZ_LCD_CFG_SYNC_DIR BIT(16)
#define JZ_LCD_CFG_PS_POLARITY BIT(15)
#define JZ_LCD_CFG_CLS_POLARITY BIT(14)
#define JZ_LCD_CFG_SPL_POLARITY BIT(13)
#define JZ_LCD_CFG_REV_POLARITY BIT(12)
#define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11)
#define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10)
#define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9)
#define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8)
#define JZ_LCD_CFG_18_BIT BIT(7)
#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4))
#define JZ_LCD_CFG_MODE_GENERIC_16BIT 0
#define JZ_LCD_CFG_MODE_GENERIC_18BIT BIT(7)
#define JZ_LCD_CFG_MODE_GENERIC_24BIT BIT(6)
#define JZ_LCD_CFG_MODE_SPECIAL_TFT_1 1
#define JZ_LCD_CFG_MODE_SPECIAL_TFT_2 2
#define JZ_LCD_CFG_MODE_SPECIAL_TFT_3 3
#define JZ_LCD_CFG_MODE_TV_OUT_P 4
#define JZ_LCD_CFG_MODE_TV_OUT_I 6
#define JZ_LCD_CFG_MODE_SINGLE_COLOR_STN 8
#define JZ_LCD_CFG_MODE_SINGLE_MONOCHROME_STN 9
#define JZ_LCD_CFG_MODE_DUAL_COLOR_STN 10
#define JZ_LCD_CFG_MODE_DUAL_MONOCHROME_STN 11
#define JZ_LCD_CFG_MODE_8BIT_SERIAL 12
#define JZ_LCD_CFG_MODE_LCM 13
#define JZ_LCD_VSYNC_VPS_OFFSET 16
#define JZ_LCD_VSYNC_VPE_OFFSET 0
#define JZ_LCD_HSYNC_HPS_OFFSET 16
#define JZ_LCD_HSYNC_HPE_OFFSET 0
#define JZ_LCD_VAT_HT_OFFSET 16
#define JZ_LCD_VAT_VT_OFFSET 0
#define JZ_LCD_DAH_HDS_OFFSET 16
#define JZ_LCD_DAH_HDE_OFFSET 0
#define JZ_LCD_DAV_VDS_OFFSET 16
#define JZ_LCD_DAV_VDE_OFFSET 0
#define JZ_LCD_CTRL_BURST_4 (0x0 << 28)
#define JZ_LCD_CTRL_BURST_8 (0x1 << 28)
#define JZ_LCD_CTRL_BURST_16 (0x2 << 28)
#define JZ_LCD_CTRL_RGB555 BIT(27)
#define JZ_LCD_CTRL_OFUP BIT(26)
#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24)
#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24)
#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24)
#define JZ_LCD_CTRL_PDD_MASK (0xff << 16)
#define JZ_LCD_CTRL_EOF_IRQ BIT(13)
#define JZ_LCD_CTRL_SOF_IRQ BIT(12)
#define JZ_LCD_CTRL_OFU_IRQ BIT(11)
#define JZ_LCD_CTRL_IFU0_IRQ BIT(10)
#define JZ_LCD_CTRL_IFU1_IRQ BIT(9)
#define JZ_LCD_CTRL_DD_IRQ BIT(8)
#define JZ_LCD_CTRL_QDD_IRQ BIT(7)
#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6)
#define JZ_LCD_CTRL_LSB_FISRT BIT(5)
#define JZ_LCD_CTRL_DISABLE BIT(4)
#define JZ_LCD_CTRL_ENABLE BIT(3)
#define JZ_LCD_CTRL_BPP_1 0x0
#define JZ_LCD_CTRL_BPP_2 0x1
#define JZ_LCD_CTRL_BPP_4 0x2
#define JZ_LCD_CTRL_BPP_8 0x3
#define JZ_LCD_CTRL_BPP_15_16 0x4
#define JZ_LCD_CTRL_BPP_18_24 0x5
#define JZ_LCD_CTRL_BPP_MASK (JZ_LCD_CTRL_RGB555 | (0x7 << 0))
#define JZ_LCD_CMD_SOF_IRQ BIT(31)
#define JZ_LCD_CMD_EOF_IRQ BIT(30)
#define JZ_LCD_CMD_ENABLE_PAL BIT(28)
#define JZ_LCD_SYNC_MASK 0x3ff
#define JZ_LCD_STATE_EOF_IRQ BIT(5)
#define JZ_LCD_STATE_SOF_IRQ BIT(4)
#define JZ_LCD_STATE_DISABLED BIT(0)
struct ingenic_dma_hwdesc {
u32 next;
u32 addr;
u32 id;
u32 cmd;
} __packed;
struct jz_soc_info {
bool needs_dev_clk;
};
struct ingenic_drm {
struct drm_device drm;
struct drm_plane primary;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct device *dev;
struct regmap *map;
struct clk *lcd_clk, *pix_clk;
struct ingenic_dma_hwdesc *dma_hwdesc;
dma_addr_t dma_hwdesc_phys;
};
static const u32 ingenic_drm_primary_formats[] = {
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB8888,
};
static bool ingenic_drm_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case JZ_REG_LCD_IID:
case JZ_REG_LCD_SA0:
case JZ_REG_LCD_FID0:
case JZ_REG_LCD_CMD0:
case JZ_REG_LCD_SA1:
case JZ_REG_LCD_FID1:
case JZ_REG_LCD_CMD1:
return false;
default:
return true;
}
}
static const struct regmap_config ingenic_drm_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = JZ_REG_LCD_CMD1,
.writeable_reg = ingenic_drm_writeable_reg,
};
static inline struct ingenic_drm *drm_device_get_priv(struct drm_device *drm)
{
return container_of(drm, struct ingenic_drm, drm);
}
static inline struct ingenic_drm *drm_crtc_get_priv(struct drm_crtc *crtc)
{
return container_of(crtc, struct ingenic_drm, crtc);
}
static inline struct ingenic_drm *
drm_encoder_get_priv(struct drm_encoder *encoder)
{
return container_of(encoder, struct ingenic_drm, encoder);
}
static inline struct ingenic_drm *drm_plane_get_priv(struct drm_plane *plane)
{
return container_of(plane, struct ingenic_drm, primary);
}
static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
regmap_write(priv->map, JZ_REG_LCD_STATE, 0);
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
JZ_LCD_CTRL_ENABLE | JZ_LCD_CTRL_DISABLE,
JZ_LCD_CTRL_ENABLE);
drm_crtc_vblank_on(crtc);
}
static void ingenic_drm_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
unsigned int var;
drm_crtc_vblank_off(crtc);
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
JZ_LCD_CTRL_DISABLE, JZ_LCD_CTRL_DISABLE);
regmap_read_poll_timeout(priv->map, JZ_REG_LCD_STATE, var,
var & JZ_LCD_STATE_DISABLED,
1000, 0);
}
static void ingenic_drm_crtc_update_timings(struct ingenic_drm *priv,
struct drm_display_mode *mode)
{
unsigned int vpe, vds, vde, vt, hpe, hds, hde, ht;
vpe = mode->vsync_end - mode->vsync_start;
vds = mode->vtotal - mode->vsync_start;
vde = vds + mode->vdisplay;
vt = vde + mode->vsync_start - mode->vdisplay;
hpe = mode->hsync_end - mode->hsync_start;
hds = mode->htotal - mode->hsync_start;
hde = hds + mode->hdisplay;
ht = hde + mode->hsync_start - mode->hdisplay;
regmap_write(priv->map, JZ_REG_LCD_VSYNC,
0 << JZ_LCD_VSYNC_VPS_OFFSET |
vpe << JZ_LCD_VSYNC_VPE_OFFSET);
regmap_write(priv->map, JZ_REG_LCD_HSYNC,
0 << JZ_LCD_HSYNC_HPS_OFFSET |
hpe << JZ_LCD_HSYNC_HPE_OFFSET);
regmap_write(priv->map, JZ_REG_LCD_VAT,
ht << JZ_LCD_VAT_HT_OFFSET |
vt << JZ_LCD_VAT_VT_OFFSET);
regmap_write(priv->map, JZ_REG_LCD_DAH,
hds << JZ_LCD_DAH_HDS_OFFSET |
hde << JZ_LCD_DAH_HDE_OFFSET);
regmap_write(priv->map, JZ_REG_LCD_DAV,
vds << JZ_LCD_DAV_VDS_OFFSET |
vde << JZ_LCD_DAV_VDE_OFFSET);
}
static void ingenic_drm_crtc_update_ctrl(struct ingenic_drm *priv,
const struct drm_format_info *finfo)
{
unsigned int ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16;
switch (finfo->format) {
case DRM_FORMAT_XRGB1555:
ctrl |= JZ_LCD_CTRL_RGB555;
/* fall-through */
case DRM_FORMAT_RGB565:
ctrl |= JZ_LCD_CTRL_BPP_15_16;
break;
case DRM_FORMAT_XRGB8888:
ctrl |= JZ_LCD_CTRL_BPP_18_24;
break;
}
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16 |
JZ_LCD_CTRL_BPP_MASK, ctrl);
}
static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
long rate;
if (!drm_atomic_crtc_needs_modeset(state))
return 0;
rate = clk_round_rate(priv->pix_clk,
state->adjusted_mode.clock * 1000);
if (rate < 0)
return rate;
return 0;
}
static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *oldstate)
{
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
struct drm_crtc_state *state = crtc->state;
struct drm_pending_vblank_event *event = state->event;
struct drm_framebuffer *drm_fb = crtc->primary->state->fb;
const struct drm_format_info *finfo;
if (drm_atomic_crtc_needs_modeset(state)) {
finfo = drm_format_info(drm_fb->format->format);
ingenic_drm_crtc_update_timings(priv, &state->mode);
ingenic_drm_crtc_update_ctrl(priv, finfo);
clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000);
regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc->next);
}
if (event) {
state->event = NULL;
spin_lock_irq(&crtc->dev->event_lock);
if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock);
}
}
static void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *oldstate)
{
struct ingenic_drm *priv = drm_plane_get_priv(plane);
struct drm_plane_state *state = plane->state;
unsigned int width, height, cpp;
width = state->crtc->state->adjusted_mode.hdisplay;
height = state->crtc->state->adjusted_mode.vdisplay;
cpp = state->fb->format->cpp[plane->index];
priv->dma_hwdesc->addr = drm_fb_cma_get_gem_addr(state->fb, state, 0);
priv->dma_hwdesc->cmd = width * height * cpp / 4;
priv->dma_hwdesc->cmd |= JZ_LCD_CMD_EOF_IRQ;
}
static void ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct ingenic_drm *priv = drm_encoder_get_priv(encoder);
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
struct drm_display_info *info = &conn_state->connector->display_info;
unsigned int cfg = JZ_LCD_CFG_PS_DISABLE
| JZ_LCD_CFG_CLS_DISABLE
| JZ_LCD_CFG_SPL_DISABLE
| JZ_LCD_CFG_REV_DISABLE;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW;
if (info->bus_flags & DRM_BUS_FLAG_DE_LOW)
cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW;
if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE)
cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE;
if (conn_state->connector->connector_type == DRM_MODE_CONNECTOR_TV) {
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
cfg |= JZ_LCD_CFG_MODE_TV_OUT_I;
else
cfg |= JZ_LCD_CFG_MODE_TV_OUT_P;
} else {
switch (*info->bus_formats) {
case MEDIA_BUS_FMT_RGB565_1X16:
cfg |= JZ_LCD_CFG_MODE_GENERIC_16BIT;
break;
case MEDIA_BUS_FMT_RGB666_1X18:
cfg |= JZ_LCD_CFG_MODE_GENERIC_18BIT;
break;
case MEDIA_BUS_FMT_RGB888_1X24:
cfg |= JZ_LCD_CFG_MODE_GENERIC_24BIT;
break;
default:
break;
}
}
regmap_write(priv->map, JZ_REG_LCD_CFG, cfg);
}
static int ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct drm_display_info *info = &conn_state->connector->display_info;
if (info->num_bus_formats != 1)
return -EINVAL;
if (conn_state->connector->connector_type == DRM_MODE_CONNECTOR_TV)
return 0;
switch (*info->bus_formats) {
case MEDIA_BUS_FMT_RGB565_1X16:
case MEDIA_BUS_FMT_RGB666_1X18:
case MEDIA_BUS_FMT_RGB888_1X24:
return 0;
default:
return -EINVAL;
}
}
static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
{
struct ingenic_drm *priv = arg;
unsigned int state;
regmap_read(priv->map, JZ_REG_LCD_STATE, &state);
regmap_update_bits(priv->map, JZ_REG_LCD_STATE,
JZ_LCD_STATE_EOF_IRQ, 0);
if (state & JZ_LCD_STATE_EOF_IRQ)
drm_crtc_handle_vblank(&priv->crtc);
return IRQ_HANDLED;
}
static void ingenic_drm_release(struct drm_device *drm)
{
struct ingenic_drm *priv = drm_device_get_priv(drm);
drm_mode_config_cleanup(drm);
drm_dev_fini(drm);
kfree(priv);
}
static int ingenic_drm_enable_vblank(struct drm_crtc *crtc)
{
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
JZ_LCD_CTRL_EOF_IRQ, JZ_LCD_CTRL_EOF_IRQ);
return 0;
}
static void ingenic_drm_disable_vblank(struct drm_crtc *crtc)
{
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, JZ_LCD_CTRL_EOF_IRQ, 0);
}
DEFINE_DRM_GEM_CMA_FOPS(ingenic_drm_fops);
static struct drm_driver ingenic_drm_driver_data = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
| DRIVER_ATOMIC,
.name = "ingenic-drm",
.desc = "DRM module for Ingenic SoCs",
.date = "20190422",
.major = 1,
.minor = 0,
.patchlevel = 0,
.fops = &ingenic_drm_fops,
.dumb_create = drm_gem_cma_dumb_create,
.gem_free_object_unlocked = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
.gem_prime_vmap = drm_gem_cma_prime_vmap,
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
.gem_prime_mmap = drm_gem_cma_prime_mmap,
.irq_handler = ingenic_drm_irq_handler,
.release = ingenic_drm_release,
};
static const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.reset = drm_atomic_helper_plane_reset,
.destroy = drm_plane_cleanup,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
static const struct drm_crtc_funcs ingenic_drm_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.destroy = drm_crtc_cleanup,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = ingenic_drm_enable_vblank,
.disable_vblank = ingenic_drm_disable_vblank,
.gamma_set = drm_atomic_helper_legacy_gamma_set,
};
static const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = {
.atomic_update = ingenic_drm_plane_atomic_update,
.prepare_fb = drm_gem_fb_prepare_fb,
};
static const struct drm_crtc_helper_funcs ingenic_drm_crtc_helper_funcs = {
.atomic_enable = ingenic_drm_crtc_atomic_enable,
.atomic_disable = ingenic_drm_crtc_atomic_disable,
.atomic_flush = ingenic_drm_crtc_atomic_flush,
.atomic_check = ingenic_drm_crtc_atomic_check,
};
static const struct drm_encoder_helper_funcs ingenic_drm_encoder_helper_funcs = {
.atomic_mode_set = ingenic_drm_encoder_atomic_mode_set,
.atomic_check = ingenic_drm_encoder_atomic_check,
};
static const struct drm_mode_config_funcs ingenic_drm_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,
};
static const struct drm_encoder_funcs ingenic_drm_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static void ingenic_drm_free_dma_hwdesc(void *d)
{
struct ingenic_drm *priv = d;
dma_free_coherent(priv->dev, sizeof(*priv->dma_hwdesc),
priv->dma_hwdesc, priv->dma_hwdesc_phys);
}
static int ingenic_drm_probe(struct platform_device *pdev)
{
const struct jz_soc_info *soc_info;
struct device *dev = &pdev->dev;
struct ingenic_drm *priv;
struct clk *parent_clk;
struct drm_bridge *bridge;
struct drm_panel *panel;
struct drm_device *drm;
struct resource *mem;
void __iomem *base;
long parent_rate;
int ret, irq;
soc_info = of_device_get_match_data(dev);
if (!soc_info) {
dev_err(dev, "Missing platform data\n");
return -EINVAL;
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
drm = &priv->drm;
drm->dev_private = priv;
platform_set_drvdata(pdev, priv);
ret = devm_drm_dev_init(dev, drm, &ingenic_drm_driver_data);
if (ret) {
kfree(priv);
return ret;
}
drm_mode_config_init(drm);
drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
drm->mode_config.max_width = 800;
drm->mode_config.max_height = 600;
drm->mode_config.funcs = &ingenic_drm_mode_config_funcs;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, mem);
if (IS_ERR(base)) {
dev_err(dev, "Failed to get memory resource");
return PTR_ERR(base);
}
priv->map = devm_regmap_init_mmio(dev, base,
&ingenic_drm_regmap_config);
if (IS_ERR(priv->map)) {
dev_err(dev, "Failed to create regmap");
return PTR_ERR(priv->map);
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "Failed to get platform irq");
return irq;
}
if (soc_info->needs_dev_clk) {
priv->lcd_clk = devm_clk_get(dev, "lcd");
if (IS_ERR(priv->lcd_clk)) {
dev_err(dev, "Failed to get lcd clock");
return PTR_ERR(priv->lcd_clk);
}
}
priv->pix_clk = devm_clk_get(dev, "lcd_pclk");
if (IS_ERR(priv->pix_clk)) {
dev_err(dev, "Failed to get pixel clock");
return PTR_ERR(priv->pix_clk);
}
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, &bridge);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get panel handle");
return ret;
}
if (panel) {
bridge = devm_drm_panel_bridge_add(dev, panel,
DRM_MODE_CONNECTOR_Unknown);
}
priv->dma_hwdesc = dma_alloc_coherent(dev, sizeof(*priv->dma_hwdesc),
&priv->dma_hwdesc_phys,
GFP_KERNEL);
if (!priv->dma_hwdesc)
return -ENOMEM;
ret = devm_add_action_or_reset(dev, ingenic_drm_free_dma_hwdesc, priv);
if (ret)
return ret;
priv->dma_hwdesc->next = priv->dma_hwdesc_phys;
priv->dma_hwdesc->id = 0xdeafbead;
drm_plane_helper_add(&priv->primary, &ingenic_drm_plane_helper_funcs);
ret = drm_universal_plane_init(drm, &priv->primary,
0, &ingenic_drm_primary_plane_funcs,
ingenic_drm_primary_formats,
ARRAY_SIZE(ingenic_drm_primary_formats),
NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
dev_err(dev, "Failed to register primary plane: %i", ret);
return ret;
}
drm_crtc_helper_add(&priv->crtc, &ingenic_drm_crtc_helper_funcs);
ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->primary,
NULL, &ingenic_drm_crtc_funcs, NULL);
if (ret) {
dev_err(dev, "Failed to init CRTC: %i", ret);
return ret;
}
priv->encoder.possible_crtcs = 1;
drm_encoder_helper_add(&priv->encoder,
&ingenic_drm_encoder_helper_funcs);
ret = drm_encoder_init(drm, &priv->encoder, &ingenic_drm_encoder_funcs,
DRM_MODE_ENCODER_DPI, NULL);
if (ret) {
dev_err(dev, "Failed to init encoder: %i", ret);
return ret;
}
ret = drm_bridge_attach(&priv->encoder, bridge, NULL);
if (ret) {
dev_err(dev, "Unable to attach bridge");
return ret;
}
ret = drm_irq_install(drm, irq);
if (ret) {
dev_err(dev, "Unable to install IRQ handler");
return ret;
}
ret = drm_vblank_init(drm, 1);
if (ret) {
dev_err(dev, "Failed calling drm_vblank_init()");
return ret;
}
drm_mode_config_reset(drm);
ret = clk_prepare_enable(priv->pix_clk);
if (ret) {
dev_err(dev, "Unable to start pixel clock");
return ret;
}
if (priv->lcd_clk) {
parent_clk = clk_get_parent(priv->lcd_clk);
parent_rate = clk_get_rate(parent_clk);
/* LCD Device clock must be 3x the pixel clock for STN panels,
* or 1.5x the pixel clock for TFT panels. To avoid having to
* check for the LCD device clock everytime we do a mode change,
* we set the LCD device clock to the highest rate possible.
*/
ret = clk_set_rate(priv->lcd_clk, parent_rate);
if (ret) {
dev_err(dev, "Unable to set LCD clock rate");
goto err_pixclk_disable;
}
ret = clk_prepare_enable(priv->lcd_clk);
if (ret) {
dev_err(dev, "Unable to start lcd clock");
goto err_pixclk_disable;
}
}
ret = drm_dev_register(drm, 0);
if (ret) {
dev_err(dev, "Failed to register DRM driver");
goto err_devclk_disable;
}
ret = drm_fbdev_generic_setup(drm, 32);
if (ret)
dev_warn(dev, "Unable to start fbdev emulation: %i", ret);
return 0;
err_devclk_disable:
if (priv->lcd_clk)
clk_disable_unprepare(priv->lcd_clk);
err_pixclk_disable:
clk_disable_unprepare(priv->pix_clk);
return ret;
}
static int ingenic_drm_remove(struct platform_device *pdev)
{
struct ingenic_drm *priv = platform_get_drvdata(pdev);
if (priv->lcd_clk)
clk_disable_unprepare(priv->lcd_clk);
clk_disable_unprepare(priv->pix_clk);
drm_dev_unregister(&priv->drm);
drm_atomic_helper_shutdown(&priv->drm);
return 0;
}
static const struct jz_soc_info jz4740_soc_info = {
.needs_dev_clk = true,
};
static const struct jz_soc_info jz4725b_soc_info = {
.needs_dev_clk = false,
};
static const struct of_device_id ingenic_drm_of_match[] = {
{ .compatible = "ingenic,jz4740-lcd", .data = &jz4740_soc_info },
{ .compatible = "ingenic,jz4725b-lcd", .data = &jz4725b_soc_info },
{ /* sentinel */ },
};
static struct platform_driver ingenic_drm_driver = {
.driver = {
.name = "ingenic-drm",
.of_match_table = of_match_ptr(ingenic_drm_of_match),
},
.probe = ingenic_drm_probe,
.remove = ingenic_drm_remove,
};
module_platform_driver(ingenic_drm_driver);
MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
MODULE_DESCRIPTION("DRM driver for the Ingenic SoCs\n");
MODULE_LICENSE("GPL v2");

View File

@ -7,6 +7,7 @@ panfrost-y := \
panfrost_gem.o \
panfrost_gpu.o \
panfrost_job.o \
panfrost_mmu.o
panfrost_mmu.o \
panfrost_perfcnt.o
obj-$(CONFIG_DRM_PANFROST) += panfrost.o

View File

@ -14,6 +14,7 @@
#include "panfrost_gpu.h"
#include "panfrost_job.h"
#include "panfrost_mmu.h"
#include "panfrost_perfcnt.h"
static int panfrost_reset_init(struct panfrost_device *pfdev)
{
@ -171,7 +172,13 @@ int panfrost_device_init(struct panfrost_device *pfdev)
pm_runtime_mark_last_busy(pfdev->dev);
pm_runtime_put_autosuspend(pfdev->dev);
err = panfrost_perfcnt_init(pfdev);
if (err)
goto err_out5;
return 0;
err_out5:
panfrost_job_fini(pfdev);
err_out4:
panfrost_mmu_fini(pfdev);
err_out3:
@ -187,6 +194,7 @@ err_out0:
void panfrost_device_fini(struct panfrost_device *pfdev)
{
panfrost_perfcnt_fini(pfdev);
panfrost_job_fini(pfdev);
panfrost_mmu_fini(pfdev);
panfrost_gpu_fini(pfdev);

View File

@ -14,6 +14,7 @@ struct panfrost_device;
struct panfrost_mmu;
struct panfrost_job_slot;
struct panfrost_job;
struct panfrost_perfcnt;
#define NUM_JOB_SLOTS 3
@ -78,6 +79,8 @@ struct panfrost_device {
struct panfrost_job *jobs[NUM_JOB_SLOTS];
struct list_head scheduled_jobs;
struct panfrost_perfcnt *perfcnt;
struct mutex sched_lock;
struct mutex reset_lock;
@ -110,11 +113,18 @@ static inline int panfrost_model_cmp(struct panfrost_device *pfdev, s32 id)
return match_id - id;
}
static inline bool panfrost_model_is_bifrost(struct panfrost_device *pfdev)
{
return panfrost_model_cmp(pfdev, 0x1000) >= 0;
}
static inline bool panfrost_model_eq(struct panfrost_device *pfdev, s32 id)
{
return !panfrost_model_cmp(pfdev, id);
}
int panfrost_unstable_ioctl_check(void);
int panfrost_device_init(struct panfrost_device *pfdev);
void panfrost_device_fini(struct panfrost_device *pfdev);

View File

@ -19,6 +19,10 @@
#include "panfrost_mmu.h"
#include "panfrost_job.h"
#include "panfrost_gpu.h"
#include "panfrost_perfcnt.h"
static bool unstable_ioctls;
module_param_unsafe(unstable_ioctls, bool, 0600);
static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct drm_file *file)
{
@ -297,6 +301,14 @@ static int panfrost_ioctl_get_bo_offset(struct drm_device *dev, void *data,
return 0;
}
int panfrost_unstable_ioctl_check(void)
{
if (!unstable_ioctls)
return -ENOSYS;
return 0;
}
static int
panfrost_open(struct drm_device *dev, struct drm_file *file)
{
@ -318,6 +330,7 @@ panfrost_postclose(struct drm_device *dev, struct drm_file *file)
{
struct panfrost_file_priv *panfrost_priv = file->driver_priv;
panfrost_perfcnt_close(panfrost_priv);
panfrost_job_close(panfrost_priv);
kfree(panfrost_priv);
@ -337,6 +350,8 @@ static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = {
PANFROST_IOCTL(MMAP_BO, mmap_bo, DRM_RENDER_ALLOW),
PANFROST_IOCTL(GET_PARAM, get_param, DRM_RENDER_ALLOW),
PANFROST_IOCTL(GET_BO_OFFSET, get_bo_offset, DRM_RENDER_ALLOW),
PANFROST_IOCTL(PERFCNT_ENABLE, perfcnt_enable, DRM_RENDER_ALLOW),
PANFROST_IOCTL(PERFCNT_DUMP, perfcnt_dump, DRM_RENDER_ALLOW),
};
DEFINE_DRM_GEM_SHMEM_FOPS(panfrost_drm_driver_fops);

View File

@ -52,6 +52,7 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t
int ret;
struct panfrost_device *pfdev = dev->dev_private;
struct panfrost_gem_object *obj;
u64 align;
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (!obj)
@ -59,9 +60,12 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t
obj->base.base.funcs = &panfrost_gem_funcs;
size = roundup(size, PAGE_SIZE);
align = size >= SZ_2M ? SZ_2M >> PAGE_SHIFT : 0;
spin_lock(&pfdev->mm_lock);
ret = drm_mm_insert_node(&pfdev->mm, &obj->node,
roundup(size, PAGE_SIZE) >> PAGE_SHIFT);
ret = drm_mm_insert_node_generic(&pfdev->mm, &obj->node,
size >> PAGE_SHIFT, align, 0, 0);
spin_unlock(&pfdev->mm_lock);
if (ret)
goto free_obj;

View File

@ -15,11 +15,9 @@
#include "panfrost_features.h"
#include "panfrost_issues.h"
#include "panfrost_gpu.h"
#include "panfrost_perfcnt.h"
#include "panfrost_regs.h"
#define gpu_write(dev, reg, data) writel(data, dev->iomem + reg)
#define gpu_read(dev, reg) readl(dev->iomem + reg)
static irqreturn_t panfrost_gpu_irq_handler(int irq, void *data)
{
struct panfrost_device *pfdev = data;
@ -43,6 +41,12 @@ static irqreturn_t panfrost_gpu_irq_handler(int irq, void *data)
gpu_write(pfdev, GPU_INT_MASK, 0);
}
if (state & GPU_IRQ_PERFCNT_SAMPLE_COMPLETED)
panfrost_perfcnt_sample_done(pfdev);
if (state & GPU_IRQ_CLEAN_CACHES_COMPLETED)
panfrost_perfcnt_clean_cache_done(pfdev);
gpu_write(pfdev, GPU_INT_CLEAR, state);
return IRQ_HANDLED;

View File

@ -0,0 +1,329 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 Collabora Ltd */
#include <drm/drm_file.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/panfrost_drm.h>
#include <linux/completion.h>
#include <linux/iopoll.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "panfrost_device.h"
#include "panfrost_features.h"
#include "panfrost_gem.h"
#include "panfrost_issues.h"
#include "panfrost_job.h"
#include "panfrost_mmu.h"
#include "panfrost_regs.h"
#define COUNTERS_PER_BLOCK 64
#define BYTES_PER_COUNTER 4
#define BLOCKS_PER_COREGROUP 8
#define V4_SHADERS_PER_COREGROUP 4
struct panfrost_perfcnt {
struct panfrost_gem_object *bo;
size_t bosize;
void *buf;
struct panfrost_file_priv *user;
struct mutex lock;
struct completion dump_comp;
};
void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
{
complete(&pfdev->perfcnt->dump_comp);
}
void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
{
gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
}
static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev)
{
u64 gpuva;
int ret;
reinit_completion(&pfdev->perfcnt->dump_comp);
gpuva = pfdev->perfcnt->bo->node.start << PAGE_SHIFT;
gpu_write(pfdev, GPU_PERFCNT_BASE_LO, gpuva);
gpu_write(pfdev, GPU_PERFCNT_BASE_HI, gpuva >> 32);
gpu_write(pfdev, GPU_INT_CLEAR,
GPU_IRQ_CLEAN_CACHES_COMPLETED |
GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
msecs_to_jiffies(1000));
if (!ret)
ret = -ETIMEDOUT;
else if (ret > 0)
ret = 0;
return ret;
}
static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
struct panfrost_file_priv *user,
unsigned int counterset)
{
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
struct drm_gem_shmem_object *bo;
u32 cfg;
int ret;
if (user == perfcnt->user)
return 0;
else if (perfcnt->user)
return -EBUSY;
ret = pm_runtime_get_sync(pfdev->dev);
if (ret < 0)
return ret;
bo = drm_gem_shmem_create(pfdev->ddev, perfcnt->bosize);
if (IS_ERR(bo))
return PTR_ERR(bo);
perfcnt->bo = to_panfrost_bo(&bo->base);
/* Map the perfcnt buf in the address space attached to file_priv. */
ret = panfrost_mmu_map(perfcnt->bo);
if (ret)
goto err_put_bo;
perfcnt->buf = drm_gem_shmem_vmap(&bo->base);
if (IS_ERR(perfcnt->buf)) {
ret = PTR_ERR(perfcnt->buf);
goto err_put_bo;
}
/*
* Invalidate the cache and clear the counters to start from a fresh
* state.
*/
reinit_completion(&pfdev->perfcnt->dump_comp);
gpu_write(pfdev, GPU_INT_CLEAR,
GPU_IRQ_CLEAN_CACHES_COMPLETED |
GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_INV_CACHES);
ret = wait_for_completion_timeout(&pfdev->perfcnt->dump_comp,
msecs_to_jiffies(1000));
if (!ret) {
ret = -ETIMEDOUT;
goto err_vunmap;
}
perfcnt->user = user;
/*
* Always use address space 0 for now.
* FIXME: this needs to be updated when we start using different
* address space.
*/
cfg = GPU_PERFCNT_CFG_AS(0) |
GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
/*
* Bifrost GPUs have 2 set of counters, but we're only interested by
* the first one for now.
*/
if (panfrost_model_is_bifrost(pfdev))
cfg |= GPU_PERFCNT_CFG_SETSEL(counterset);
gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
/*
* Due to PRLAM-8186 we need to disable the Tiler before we enable HW
* counters.
*/
if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
else
gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
return 0;
err_vunmap:
drm_gem_shmem_vunmap(&perfcnt->bo->base.base, perfcnt->buf);
err_put_bo:
drm_gem_object_put_unlocked(&bo->base);
return ret;
}
static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
struct panfrost_file_priv *user)
{
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
if (user != perfcnt->user)
return -EINVAL;
gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0x0);
gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0x0);
gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0x0);
gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
gpu_write(pfdev, GPU_PERFCNT_CFG,
GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
perfcnt->user = NULL;
drm_gem_shmem_vunmap(&perfcnt->bo->base.base, perfcnt->buf);
perfcnt->buf = NULL;
drm_gem_object_put_unlocked(&perfcnt->bo->base.base);
perfcnt->bo = NULL;
pm_runtime_mark_last_busy(pfdev->dev);
pm_runtime_put_autosuspend(pfdev->dev);
return 0;
}
int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct panfrost_file_priv *pfile = file_priv->driver_priv;
struct panfrost_device *pfdev = dev->dev_private;
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
struct drm_panfrost_perfcnt_enable *req = data;
int ret;
ret = panfrost_unstable_ioctl_check();
if (ret)
return ret;
/* Only Bifrost GPUs have 2 set of counters. */
if (req->counterset > (panfrost_model_is_bifrost(pfdev) ? 1 : 0))
return -EINVAL;
mutex_lock(&perfcnt->lock);
if (req->enable)
ret = panfrost_perfcnt_enable_locked(pfdev, pfile,
req->counterset);
else
ret = panfrost_perfcnt_disable_locked(pfdev, pfile);
mutex_unlock(&perfcnt->lock);
return ret;
}
int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct panfrost_device *pfdev = dev->dev_private;
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
struct drm_panfrost_perfcnt_dump *req = data;
void __user *user_ptr = (void __user *)(uintptr_t)req->buf_ptr;
int ret;
ret = panfrost_unstable_ioctl_check();
if (ret)
return ret;
mutex_lock(&perfcnt->lock);
if (perfcnt->user != file_priv->driver_priv) {
ret = -EINVAL;
goto out;
}
ret = panfrost_perfcnt_dump_locked(pfdev);
if (ret)
goto out;
if (copy_to_user(user_ptr, perfcnt->buf, perfcnt->bosize))
ret = -EFAULT;
out:
mutex_unlock(&perfcnt->lock);
return ret;
}
void panfrost_perfcnt_close(struct panfrost_file_priv *pfile)
{
struct panfrost_device *pfdev = pfile->pfdev;
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
pm_runtime_get_sync(pfdev->dev);
mutex_lock(&perfcnt->lock);
if (perfcnt->user == pfile)
panfrost_perfcnt_disable_locked(pfdev, pfile);
mutex_unlock(&perfcnt->lock);
pm_runtime_mark_last_busy(pfdev->dev);
pm_runtime_put_autosuspend(pfdev->dev);
}
int panfrost_perfcnt_init(struct panfrost_device *pfdev)
{
struct panfrost_perfcnt *perfcnt;
size_t size;
if (panfrost_has_hw_feature(pfdev, HW_FEATURE_V4)) {
unsigned int ncoregroups;
ncoregroups = hweight64(pfdev->features.l2_present);
size = ncoregroups * BLOCKS_PER_COREGROUP *
COUNTERS_PER_BLOCK * BYTES_PER_COUNTER;
} else {
unsigned int nl2c, ncores;
/*
* TODO: define a macro to extract the number of l2 caches from
* mem_features.
*/
nl2c = ((pfdev->features.mem_features >> 8) & GENMASK(3, 0)) + 1;
/*
* shader_present might be sparse, but the counters layout
* forces to dump unused regions too, hence the fls64() call
* instead of hweight64().
*/
ncores = fls64(pfdev->features.shader_present);
/*
* There's always one JM and one Tiler block, hence the '+ 2'
* here.
*/
size = (nl2c + ncores + 2) *
COUNTERS_PER_BLOCK * BYTES_PER_COUNTER;
}
perfcnt = devm_kzalloc(pfdev->dev, sizeof(*perfcnt), GFP_KERNEL);
if (!perfcnt)
return -ENOMEM;
perfcnt->bosize = size;
/* Start with everything disabled. */
gpu_write(pfdev, GPU_PERFCNT_CFG,
GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0);
gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0);
gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0);
gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
init_completion(&perfcnt->dump_comp);
mutex_init(&perfcnt->lock);
pfdev->perfcnt = perfcnt;
return 0;
}
void panfrost_perfcnt_fini(struct panfrost_device *pfdev)
{
/* Disable everything before leaving. */
gpu_write(pfdev, GPU_PERFCNT_CFG,
GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0);
gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0);
gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0);
gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
}

View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright 2019 Collabora Ltd */
#ifndef __PANFROST_PERFCNT_H__
#define __PANFROST_PERFCNT_H__
#include "panfrost_device.h"
void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev);
void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev);
int panfrost_perfcnt_init(struct panfrost_device *pfdev);
void panfrost_perfcnt_fini(struct panfrost_device *pfdev);
void panfrost_perfcnt_close(struct panfrost_file_priv *pfile);
int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
struct drm_file *file_priv);
#endif

View File

@ -44,12 +44,31 @@
GPU_IRQ_MULTIPLE_FAULT)
#define GPU_CMD 0x30
#define GPU_CMD_SOFT_RESET 0x01
#define GPU_CMD_PERFCNT_CLEAR 0x03
#define GPU_CMD_PERFCNT_SAMPLE 0x04
#define GPU_CMD_CLEAN_CACHES 0x07
#define GPU_CMD_CLEAN_INV_CACHES 0x08
#define GPU_STATUS 0x34
#define GPU_STATUS_PRFCNT_ACTIVE BIT(2)
#define GPU_LATEST_FLUSH_ID 0x38
#define GPU_FAULT_STATUS 0x3C
#define GPU_FAULT_ADDRESS_LO 0x40
#define GPU_FAULT_ADDRESS_HI 0x44
#define GPU_PERFCNT_BASE_LO 0x60
#define GPU_PERFCNT_BASE_HI 0x64
#define GPU_PERFCNT_CFG 0x68
#define GPU_PERFCNT_CFG_MODE(x) (x)
#define GPU_PERFCNT_CFG_MODE_OFF 0
#define GPU_PERFCNT_CFG_MODE_MANUAL 1
#define GPU_PERFCNT_CFG_MODE_TILE 2
#define GPU_PERFCNT_CFG_AS(x) ((x) << 4)
#define GPU_PERFCNT_CFG_SETSEL(x) ((x) << 8)
#define GPU_PRFCNT_JM_EN 0x6c
#define GPU_PRFCNT_SHADER_EN 0x70
#define GPU_PRFCNT_TILER_EN 0x74
#define GPU_PRFCNT_MMU_L2_EN 0x7c
#define GPU_THREAD_MAX_THREADS 0x0A0 /* (RO) Maximum number of threads per core */
#define GPU_THREAD_MAX_WORKGROUP_SIZE 0x0A4 /* (RO) Maximum workgroup size */
#define GPU_THREAD_MAX_BARRIER_SIZE 0x0A8 /* (RO) Maximum threads waiting at a barrier */
@ -295,4 +314,7 @@
#define AS_FAULTSTATUS_ACCESS_TYPE_READ (0x2 << 8)
#define AS_FAULTSTATUS_ACCESS_TYPE_WRITE (0x3 << 8)
#define gpu_write(dev, reg, data) writel(data, dev->iomem + reg)
#define gpu_read(dev, reg) readl(dev->iomem + reg)
#endif

View File

@ -115,8 +115,8 @@ static int rcar_lvds_connector_atomic_check(struct drm_connector *connector,
/* We're not allowed to modify the resolution. */
crtc_state = drm_atomic_get_crtc_state(state, conn_state->crtc);
if (!crtc_state)
return -EINVAL;
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
if (crtc_state->mode.hdisplay != panel_mode->hdisplay ||
crtc_state->mode.vdisplay != panel_mode->vdisplay)

View File

@ -535,7 +535,7 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
if (ret)
goto err_get_training_status;
dp->link.rate = status[0];
dp->link.rate = drm_dp_bw_code_to_link_rate(status[0]);
dp->link.num_lanes = status[1];
err_get_training_status:
@ -639,7 +639,7 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
(video->color_depth * 2) : (video->color_depth * 3);
link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate) / 1000;
link_rate = dp->link.rate / 1000;
ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE);
if (ret)

View File

@ -19,6 +19,14 @@
#include "rockchip_drm_drv.h"
#include "rockchip_drm_vop.h"
#define RK3228_GRF_SOC_CON2 0x0408
#define RK3228_HDMI_SDAIN_MSK BIT(14)
#define RK3228_HDMI_SCLIN_MSK BIT(13)
#define RK3228_GRF_SOC_CON6 0x0418
#define RK3228_HDMI_HPD_VSEL BIT(6)
#define RK3228_HDMI_SDA_VSEL BIT(5)
#define RK3228_HDMI_SCL_VSEL BIT(4)
#define RK3288_GRF_SOC_CON6 0x025C
#define RK3288_HDMI_LCDC_SEL BIT(4)
#define RK3328_GRF_SOC_CON2 0x0408
@ -321,6 +329,25 @@ static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data)
phy_power_off(hdmi->phy);
}
static void dw_hdmi_rk3228_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
{
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
dw_hdmi_phy_setup_hpd(dw_hdmi, data);
regmap_write(hdmi->regmap,
RK3228_GRF_SOC_CON6,
HIWORD_UPDATE(RK3228_HDMI_HPD_VSEL | RK3228_HDMI_SDA_VSEL |
RK3228_HDMI_SCL_VSEL,
RK3228_HDMI_HPD_VSEL | RK3228_HDMI_SDA_VSEL |
RK3228_HDMI_SCL_VSEL));
regmap_write(hdmi->regmap,
RK3228_GRF_SOC_CON2,
HIWORD_UPDATE(RK3228_HDMI_SDAIN_MSK | RK3228_HDMI_SCLIN_MSK,
RK3228_HDMI_SDAIN_MSK | RK3228_HDMI_SCLIN_MSK));
}
static enum drm_connector_status
dw_hdmi_rk3328_read_hpd(struct dw_hdmi *dw_hdmi, void *data)
{
@ -366,6 +393,29 @@ static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
RK3328_HDMI_HPD_IOE));
}
static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = {
.init = dw_hdmi_rockchip_genphy_init,
.disable = dw_hdmi_rockchip_genphy_disable,
.read_hpd = dw_hdmi_phy_read_hpd,
.update_hpd = dw_hdmi_phy_update_hpd,
.setup_hpd = dw_hdmi_rk3228_setup_hpd,
};
static struct rockchip_hdmi_chip_data rk3228_chip_data = {
.lcdsel_grf_reg = -1,
};
static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
.mode_valid = dw_hdmi_rockchip_mode_valid,
.mpll_cfg = rockchip_mpll_cfg,
.cur_ctr = rockchip_cur_ctr,
.phy_config = rockchip_phy_config,
.phy_data = &rk3228_chip_data,
.phy_ops = &rk3228_hdmi_phy_ops,
.phy_name = "inno_dw_hdmi_phy2",
.phy_force_vendor = true,
};
static struct rockchip_hdmi_chip_data rk3288_chip_data = {
.lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
.lcdsel_big = HIWORD_UPDATE(0, RK3288_HDMI_LCDC_SEL),
@ -418,6 +468,9 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
};
static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
{ .compatible = "rockchip,rk3228-dw-hdmi",
.data = &rk3228_hdmi_drv_data
},
{ .compatible = "rockchip,rk3288-dw-hdmi",
.data = &rk3288_hdmi_drv_data
},

View File

@ -1006,7 +1006,8 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
struct vop *vop = to_vop(crtc);
adjusted_mode->clock =
clk_round_rate(vop->dclk, mode->clock * 1000) / 1000;
DIV_ROUND_UP(clk_round_rate(vop->dclk,
adjusted_mode->clock * 1000), 1000);
return true;
}

View File

@ -3,4 +3,4 @@ test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \
test-drm_format.o test-drm_framebuffer.o \
test-drm_damage_helper.o
obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o
obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o test-drm_cmdline_parser.o

View File

@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* List each unit test as selftest(function)
*
* The name is used as both an enum and expanded as igt__name to create
* a module parameter. It must be unique and legal for a C identifier.
*
* Tests are executed in order by igt/drm_mm
*/
#define cmdline_test(test) selftest(test, test)
cmdline_test(drm_cmdline_test_res)
cmdline_test(drm_cmdline_test_res_missing_x)
cmdline_test(drm_cmdline_test_res_missing_y)
cmdline_test(drm_cmdline_test_res_bad_y)
cmdline_test(drm_cmdline_test_res_missing_y_bpp)
cmdline_test(drm_cmdline_test_res_vesa)
cmdline_test(drm_cmdline_test_res_vesa_rblank)
cmdline_test(drm_cmdline_test_res_rblank)
cmdline_test(drm_cmdline_test_res_bpp)
cmdline_test(drm_cmdline_test_res_bad_bpp)
cmdline_test(drm_cmdline_test_res_refresh)
cmdline_test(drm_cmdline_test_res_bad_refresh)
cmdline_test(drm_cmdline_test_res_bpp_refresh)
cmdline_test(drm_cmdline_test_res_bpp_refresh_interlaced)
cmdline_test(drm_cmdline_test_res_bpp_refresh_margins)
cmdline_test(drm_cmdline_test_res_bpp_refresh_force_off)
cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on_off)
cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on)
cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on_analog)
cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on_digital)
cmdline_test(drm_cmdline_test_res_bpp_refresh_interlaced_margins_force_on)
cmdline_test(drm_cmdline_test_res_margins_force_on)
cmdline_test(drm_cmdline_test_res_vesa_margins)
cmdline_test(drm_cmdline_test_res_invalid_mode)
cmdline_test(drm_cmdline_test_res_bpp_wrong_place_mode)
cmdline_test(drm_cmdline_test_name)
cmdline_test(drm_cmdline_test_name_bpp)
cmdline_test(drm_cmdline_test_name_refresh)
cmdline_test(drm_cmdline_test_name_bpp_refresh)
cmdline_test(drm_cmdline_test_name_refresh_wrong_mode)
cmdline_test(drm_cmdline_test_name_refresh_invalid_mode)
cmdline_test(drm_cmdline_test_name_option)
cmdline_test(drm_cmdline_test_name_bpp_option)
cmdline_test(drm_cmdline_test_rotate_0)
cmdline_test(drm_cmdline_test_rotate_90)
cmdline_test(drm_cmdline_test_rotate_180)
cmdline_test(drm_cmdline_test_rotate_270)
cmdline_test(drm_cmdline_test_rotate_invalid_val)
cmdline_test(drm_cmdline_test_rotate_truncated)
cmdline_test(drm_cmdline_test_hmirror)
cmdline_test(drm_cmdline_test_vmirror)
cmdline_test(drm_cmdline_test_margin_options)
cmdline_test(drm_cmdline_test_multiple_options)
cmdline_test(drm_cmdline_test_invalid_option)

View File

@ -0,0 +1,918 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 Bootlin
*/
#define pr_fmt(fmt) "drm_cmdline: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <drm/drm_connector.h>
#include <drm/drm_modes.h>
#define TESTS "drm_cmdline_selftests.h"
#include "drm_selftest.h"
#include "test-drm_modeset_common.h"
static int drm_cmdline_test_res(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_missing_x(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("x480",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_res_missing_y(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("1024x",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_res_bad_y(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("1024xtest",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_res_missing_y_bpp(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("1024x-24",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_res_vesa(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480M",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(!mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_vesa_rblank(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480MR",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(!mode.rb);
FAIL_ON(!mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_rblank(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480R",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(!mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_bpp(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.refresh_specified);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_bad_bpp(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("720x480-test",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_res_refresh(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480@60",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(!mode.refresh_specified);
FAIL_ON(mode.refresh != 60);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_bad_refresh(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("720x480@refresh",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_res_bpp_refresh(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(!mode.refresh_specified);
FAIL_ON(mode.refresh != 60);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_bpp_refresh_interlaced(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60i",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(!mode.refresh_specified);
FAIL_ON(mode.refresh != 60);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(!mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_bpp_refresh_margins(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60m",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(!mode.refresh_specified);
FAIL_ON(mode.refresh != 60);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(!mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_bpp_refresh_force_off(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60d",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(!mode.refresh_specified);
FAIL_ON(mode.refresh != 60);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_OFF);
return 0;
}
static int drm_cmdline_test_res_bpp_refresh_force_on_off(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("720x480-24@60de",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_res_bpp_refresh_force_on(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60e",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(!mode.refresh_specified);
FAIL_ON(mode.refresh != 60);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_ON);
return 0;
}
static int drm_cmdline_test_res_bpp_refresh_force_on_analog(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60D",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(!mode.refresh_specified);
FAIL_ON(mode.refresh != 60);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_ON);
return 0;
}
static int drm_cmdline_test_res_bpp_refresh_force_on_digital(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
connector.connector_type = DRM_MODE_CONNECTOR_DVII;
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60D",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(!mode.refresh_specified);
FAIL_ON(mode.refresh != 60);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_ON_DIGITAL);
return 0;
}
static int drm_cmdline_test_res_bpp_refresh_interlaced_margins_force_on(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60ime",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(!mode.refresh_specified);
FAIL_ON(mode.refresh != 60);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(!mode.interlace);
FAIL_ON(!mode.margins);
FAIL_ON(mode.force != DRM_FORCE_ON);
return 0;
}
static int drm_cmdline_test_res_margins_force_on(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480me",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(!mode.margins);
FAIL_ON(mode.force != DRM_FORCE_ON);
return 0;
}
static int drm_cmdline_test_res_vesa_margins(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480Mm",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(!mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(!mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_res_invalid_mode(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("720x480f",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_res_bpp_wrong_place_mode(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("720x480e-24",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_name(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC",
&connector,
&mode));
FAIL_ON(strcmp(mode.name, "NTSC"));
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
return 0;
}
static int drm_cmdline_test_name_bpp(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC-24",
&connector,
&mode));
FAIL_ON(strcmp(mode.name, "NTSC"));
FAIL_ON(mode.refresh_specified);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
return 0;
}
static int drm_cmdline_test_name_bpp_refresh(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC-24@60",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_name_refresh(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC@60",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_name_refresh_wrong_mode(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC@60m",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_name_refresh_invalid_mode(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC@60f",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_name_option(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC,rotate=180",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(strcmp(mode.name, "NTSC"));
FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
return 0;
}
static int drm_cmdline_test_name_bpp_option(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC-24,rotate=180",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(strcmp(mode.name, "NTSC"));
FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
FAIL_ON(!mode.bpp_specified);
FAIL_ON(mode.bpp != 24);
return 0;
}
static int drm_cmdline_test_rotate_0(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=0",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_0);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_rotate_90(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=90",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_90);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_rotate_180(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=180",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_rotate_270(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=270",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_270);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_rotate_invalid_val(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,rotate=42",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_rotate_truncated(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,rotate=",
&connector,
&mode));
return 0;
}
static int drm_cmdline_test_hmirror(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,reflect_x",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.rotation_reflection != DRM_MODE_REFLECT_X);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_vmirror(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,reflect_y",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.rotation_reflection != DRM_MODE_REFLECT_Y);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_margin_options(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.tv_margins.right != 14);
FAIL_ON(mode.tv_margins.left != 24);
FAIL_ON(mode.tv_margins.bottom != 36);
FAIL_ON(mode.tv_margins.top != 42);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_multiple_options(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=270,reflect_x",
&connector,
&mode));
FAIL_ON(!mode.specified);
FAIL_ON(mode.xres != 720);
FAIL_ON(mode.yres != 480);
FAIL_ON(mode.rotation_reflection != (DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X));
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_invalid_option(void *ignored)
{
struct drm_connector connector = { };
struct drm_cmdline_mode mode = { };
FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,test=42",
&connector,
&mode));
return 0;
}
#include "drm_selftest.c"
static int __init test_drm_cmdline_init(void)
{
int err;
err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL);
return err > 0 ? 0 : err;
}
module_init(test_drm_cmdline_init);
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
MODULE_LICENSE("GPL");

View File

@ -136,8 +136,7 @@ static __maybe_unused int drv_suspend(struct device *dev)
struct ltdc_device *ldev = ddev->dev_private;
struct drm_atomic_state *state;
if (WARN_ON(!ldev->suspend_state))
return -ENOENT;
WARN_ON(ldev->suspend_state);
state = drm_atomic_helper_suspend(ddev);
if (IS_ERR(state))
@ -155,15 +154,17 @@ static __maybe_unused int drv_resume(struct device *dev)
struct ltdc_device *ldev = ddev->dev_private;
int ret;
if (WARN_ON(!ldev->suspend_state))
return -ENOENT;
pm_runtime_force_resume(dev);
ret = drm_atomic_helper_resume(ddev, ldev->suspend_state);
if (ret) {
if (ret)
pm_runtime_force_suspend(dev);
ldev->suspend_state = NULL;
return ret;
}
return 0;
ldev->suspend_state = NULL;
return ret;
}
static __maybe_unused int drv_runtime_suspend(struct device *dev)

View File

@ -539,13 +539,13 @@ pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp)
tmp = pgprot_noncached(tmp);
#endif
#if defined(__ia64__) || defined(__arm__) || defined(__aarch64__) || \
defined(__powerpc__)
defined(__powerpc__) || defined(__mips__)
if (caching_flags & TTM_PL_FLAG_WC)
tmp = pgprot_writecombine(tmp);
else
tmp = pgprot_noncached(tmp);
#endif
#if defined(__sparc__) || defined(__mips__)
#if defined(__sparc__)
tmp = pgprot_noncached(tmp);
#endif
return tmp;

View File

@ -29,13 +29,9 @@ vc4_debugfs_init(struct drm_minor *minor)
{
struct vc4_dev *vc4 = to_vc4_dev(minor->dev);
struct vc4_debugfs_info_entry *entry;
struct dentry *dentry;
dentry = debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
minor->debugfs_root,
&vc4->load_tracker_enabled);
if (!dentry)
return -ENOMEM;
debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
minor->debugfs_root, &vc4->load_tracker_enabled);
list_for_each_entry(entry, &vc4->debugfs_list, link) {
int ret = drm_debugfs_create_files(&entry->info, 1,

View File

@ -255,11 +255,17 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
return ret;
}
static void vc4_hdmi_connector_reset(struct drm_connector *connector)
{
drm_atomic_helper_connector_reset(connector);
drm_atomic_helper_connector_tv_reset(connector);
}
static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
.detect = vc4_hdmi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = vc4_hdmi_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.reset = vc4_hdmi_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};

View File

@ -212,6 +212,15 @@ out:
spin_unlock_irqrestore(&out->state_lock, flags);
}
static const char * const pipe_crc_sources[] = {"auto"};
const char *const *vkms_get_crc_sources(struct drm_crtc *crtc,
size_t *count)
{
*count = ARRAY_SIZE(pipe_crc_sources);
return pipe_crc_sources;
}
static int vkms_crc_parse_source(const char *src_name, bool *enabled)
{
int ret = 0;

View File

@ -147,6 +147,7 @@ static const struct drm_crtc_funcs vkms_crtc_funcs = {
.atomic_destroy_state = vkms_atomic_crtc_destroy_state,
.enable_vblank = vkms_enable_vblank,
.disable_vblank = vkms_disable_vblank,
.get_crc_sources = vkms_get_crc_sources,
.set_crc_source = vkms_set_crc_source,
.verify_crc_source = vkms_verify_crc_source,
};

View File

@ -20,14 +20,6 @@
extern bool enable_cursor;
static const u32 vkms_formats[] = {
DRM_FORMAT_XRGB8888,
};
static const u32 vkms_cursor_formats[] = {
DRM_FORMAT_ARGB8888,
};
struct vkms_crc_data {
struct drm_framebuffer fb;
struct drm_rect src, dst;
@ -136,6 +128,8 @@ int vkms_gem_vmap(struct drm_gem_object *obj);
void vkms_gem_vunmap(struct drm_gem_object *obj);
/* CRC Support */
const char *const *vkms_get_crc_sources(struct drm_crtc *crtc,
size_t *count);
int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name);
int vkms_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
size_t *values_cnt);

View File

@ -6,6 +6,14 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
static const u32 vkms_formats[] = {
DRM_FORMAT_XRGB8888,
};
static const u32 vkms_cursor_formats[] = {
DRM_FORMAT_ARGB8888,
};
static struct drm_plane_state *
vkms_plane_duplicate_state(struct drm_plane *plane)
{

View File

@ -35,6 +35,7 @@
#include <linux/debugfs.h>
#include <linux/fb.h>
#include <linux/fs.h>
#include <linux/fbcon.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pm_domain.h>
@ -734,14 +735,8 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
if (!active->driver_power_control)
set_audio_state(active->id, VGA_SWITCHEROO_OFF);
if (new_client->fb_info) {
struct fb_event event;
console_lock();
event.info = new_client->fb_info;
fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);
console_unlock();
}
if (new_client->fb_info)
fbcon_remap_all(new_client->fb_info);
mutex_lock(&vgasr_priv.mux_hw_lock);
ret = vgasr_priv.handler->switchto(new_client->id);

View File

@ -1246,11 +1246,7 @@ static int ivtvfb_callback_cleanup(struct device *dev, void *p)
struct osd_info *oi = itv->osd_info;
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n",
itv->instance);
return 0;
}
unregister_framebuffer(&itv->osd_info->ivtvfb_info);
IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance);
itv->ivtvfb_restore = NULL;
ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info);

View File

@ -891,7 +891,9 @@ int fbtft_unregister_framebuffer(struct fb_info *fb_info)
if (par->fbtftops.unregister_backlight)
par->fbtftops.unregister_backlight(par);
fbtft_sysfs_exit(par);
return unregister_framebuffer(fb_info);
unregister_framebuffer(fb_info);
return 0;
}
EXPORT_SYMBOL(fbtft_unregister_framebuffer);

View File

@ -1,4 +1,11 @@
TODO:
- complete rewrite:
1. The underlying fbdev drivers need to be converted into drm kernel
modesetting drivers.
2. The dcon low-power display mode can then be integrated using the
drm damage tracking and self-refresh helpers.
This bolted-on self-refresh support that digs around in fbdev
internals, but isn't properly integrated, is not the correct solution.
- see if vx855 gpio API can be made similar enough to cs5535 so we can
share more code
- convert all uses of the old GPIO API from <linux/gpio.h> to the

View File

@ -250,11 +250,7 @@ static bool dcon_blank_fb(struct dcon_priv *dcon, bool blank)
int err;
console_lock();
if (!lock_fb_info(dcon->fbinfo)) {
console_unlock();
dev_err(&dcon->client->dev, "unable to lock framebuffer\n");
return false;
}
lock_fb_info(dcon->fbinfo);
dcon->ignore_fb_events = true;
err = fb_blank(dcon->fbinfo,

View File

@ -3822,6 +3822,8 @@ int con_is_bound(const struct consw *csw)
{
int i, bound = 0;
WARN_CONSOLE_UNLOCKED();
for (i = 0; i < MAX_NR_CONSOLES; i++) {
if (con_driver_map[i] == csw) {
bound = 1;
@ -3833,6 +3835,20 @@ int con_is_bound(const struct consw *csw)
}
EXPORT_SYMBOL(con_is_bound);
/**
* con_is_visible - checks whether the current console is visible
* @vc: virtual console
*
* RETURNS: zero if not visible, nonzero if visible
*/
bool con_is_visible(const struct vc_data *vc)
{
WARN_CONSOLE_UNLOCKED();
return *vc->vc_display_fg == vc;
}
EXPORT_SYMBOL(con_is_visible);
/**
* con_debug_enter - prepare the console for the kernel debugger
* @sw: console driver
@ -4166,6 +4182,8 @@ void do_blank_screen(int entering_gfx)
struct vc_data *vc = vc_cons[fg_console].d;
int i;
might_sleep();
WARN_CONSOLE_UNLOCKED();
if (console_blanked) {

View File

@ -47,7 +47,7 @@ static int fb_notifier_callback(struct notifier_block *self,
int fb_blank = 0;
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
if (event != FB_EVENT_BLANK)
return 0;
bd = container_of(self, struct backlight_device, fb_notif);

View File

@ -30,18 +30,6 @@ static int fb_notifier_callback(struct notifier_block *self,
struct lcd_device *ld;
struct fb_event *evdata = data;
/* If we aren't interested in this event, skip it immediately ... */
switch (event) {
case FB_EVENT_BLANK:
case FB_EVENT_MODE_CHANGE:
case FB_EVENT_MODE_CHANGE_ALL:
case FB_EARLY_EVENT_BLANK:
case FB_R_EARLY_EVENT_BLANK:
break;
default:
return 0;
}
ld = container_of(self, struct lcd_device, fb_notif);
if (!ld->ops)
return 0;

View File

@ -34,6 +34,8 @@ static bool dummycon_putc_called;
void dummycon_register_output_notifier(struct notifier_block *nb)
{
WARN_CONSOLE_UNLOCKED();
raw_notifier_chain_register(&dummycon_output_nh, nb);
if (dummycon_putc_called)
@ -42,11 +44,15 @@ void dummycon_register_output_notifier(struct notifier_block *nb)
void dummycon_unregister_output_notifier(struct notifier_block *nb)
{
WARN_CONSOLE_UNLOCKED();
raw_notifier_chain_unregister(&dummycon_output_nh, nb);
}
static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos)
{
WARN_CONSOLE_UNLOCKED();
dummycon_putc_called = true;
raw_notifier_call_chain(&dummycon_output_nh, 0, NULL);
}

View File

@ -2350,70 +2350,6 @@ static int aty128fb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
return -EINVAL;
}
#if 0
/*
* Accelerated functions
*/
static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
u_int width, u_int height,
struct fb_info_aty128 *par)
{
u32 save_dp_datatype, save_dp_cntl, dstval;
if (!width || !height)
return;
dstval = depth_to_dst(par->current_par.crtc.depth);
if (dstval == DST_24BPP) {
srcx *= 3;
dstx *= 3;
width *= 3;
} else if (dstval == -EINVAL) {
printk("aty128fb: invalid depth or RGBA\n");
return;
}
wait_for_fifo(2, par);
save_dp_datatype = aty_ld_le32(DP_DATATYPE);
save_dp_cntl = aty_ld_le32(DP_CNTL);
wait_for_fifo(6, par);
aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
par->blitter_may_be_busy = 1;
wait_for_fifo(2, par);
aty_st_le32(DP_DATATYPE, save_dp_datatype);
aty_st_le32(DP_CNTL, save_dp_cntl);
}
/*
* Text mode accelerated functions
*/
static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy,
int dx, int height, int width)
{
sx *= fontwidth(p);
sy *= fontheight(p);
dx *= fontwidth(p);
dy *= fontheight(p);
width *= fontwidth(p);
height *= fontheight(p);
aty128_rectcopy(sx, sy, dx, dy, width, height,
(struct fb_info_aty128 *)p->fb_info);
}
#endif /* 0 */
static void aty128_set_suspend(struct aty128fb_par *par, int suspend)
{
u32 pmgt;

View File

@ -3916,8 +3916,7 @@ static int atyfb_reboot_notify(struct notifier_block *nb,
if (!reboot_info)
goto out;
if (!lock_fb_info(reboot_info))
goto out;
lock_fb_info(reboot_info);
par = reboot_info->par;

View File

@ -285,11 +285,7 @@ int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
goto out;
}
umap.start = cmap->start;
if (!lock_fb_info(info)) {
rc = -ENODEV;
goto out;
}
lock_fb_info(info);
rc = fb_set_cmap(&umap, info);
unlock_fb_info(info);
out:

View File

@ -76,6 +76,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/crc32.h> /* For counting font checksums */
#include <linux/uaccess.h>
#include <asm/fb.h>
#include <asm/irq.h>
@ -87,13 +88,32 @@
# define DPRINTK(fmt, args...)
#endif
/*
* FIXME: Locking
*
* - fbcon state itself is protected by the console_lock, and the code does a
* pretty good job at making sure that lock is held everywhere it's needed.
*
* - access to the registered_fb array is entirely unprotected. This should use
* proper object lifetime handling, i.e. get/put_fb_info. This also means
* switching from indices to proper pointers for fb_info everywhere.
*
* - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it
* means concurrent access to the same fbdev from both fbcon and userspace
* will blow up. To fix this all fbcon calls from fbmem.c need to be moved out
* of fb_lock/unlock protected sections, since otherwise we'll recurse and
* deadlock eventually. Aside: Due to these deadlock issues the fbdev code in
* fbmem.c cannot use locking asserts, and there's lots of callers which get
* the rules wrong, e.g. fbsysfs.c entirely missed fb_lock/unlock calls too.
*/
enum {
FBCON_LOGO_CANSHOW = -1, /* the logo can be shown */
FBCON_LOGO_DRAW = -2, /* draw the logo to a console */
FBCON_LOGO_DONTSHOW = -3 /* do not show the logo */
};
static struct display fb_display[MAX_NR_CONSOLES];
static struct fbcon_display fb_display[MAX_NR_CONSOLES];
static signed char con2fb_map[MAX_NR_CONSOLES];
static signed char con2fb_map_boot[MAX_NR_CONSOLES];
@ -112,7 +132,6 @@ static int softback_lines;
static int first_fb_vc;
static int last_fb_vc = MAX_NR_CONSOLES - 1;
static int fbcon_is_default = 1;
static int fbcon_has_exited;
static int primary_device = -1;
static int fbcon_has_console_bind;
@ -185,11 +204,11 @@ static __inline__ void ywrap_up(struct vc_data *vc, int count);
static __inline__ void ywrap_down(struct vc_data *vc, int count);
static __inline__ void ypan_up(struct vc_data *vc, int count);
static __inline__ void ypan_down(struct vc_data *vc, int count);
static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
int dy, int dx, int height, int width, u_int y_break);
static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
int unit);
static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
int line, int count, int dy);
static void fbcon_modechanged(struct fb_info *info);
static void fbcon_set_all_vcs(struct fb_info *info);
@ -220,7 +239,7 @@ static void fbcon_rotate(struct fb_info *info, u32 rotate)
fb_info = registered_fb[con2fb_map[ops->currcon]];
if (info == fb_info) {
struct display *p = &fb_display[ops->currcon];
struct fbcon_display *p = &fb_display[ops->currcon];
if (rotate < 4)
p->con_rotate = rotate;
@ -235,7 +254,7 @@ static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
{
struct fbcon_ops *ops = info->fbcon_par;
struct vc_data *vc;
struct display *p;
struct fbcon_display *p;
int i;
if (!ops || ops->currcon < 0 || rotate > 3)
@ -900,7 +919,7 @@ static int set_con2fb_map(int unit, int newidx, int user)
* Low Level Operations
*/
/* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */
static int var_to_display(struct display *disp,
static int var_to_display(struct fbcon_display *disp,
struct fb_var_screeninfo *var,
struct fb_info *info)
{
@ -925,7 +944,7 @@ static int var_to_display(struct display *disp,
}
static void display_to_var(struct fb_var_screeninfo *var,
struct display *disp)
struct fbcon_display *disp)
{
fb_videomode_to_var(var, disp->mode);
var->xres_virtual = disp->xres_virtual;
@ -946,7 +965,7 @@ static void display_to_var(struct fb_var_screeninfo *var,
static const char *fbcon_startup(void)
{
const char *display_desc = "frame buffer device";
struct display *p = &fb_display[fg_console];
struct fbcon_display *p = &fb_display[fg_console];
struct vc_data *vc = vc_cons[fg_console].d;
const struct font_desc *font = NULL;
struct module *owner;
@ -1050,23 +1069,26 @@ static const char *fbcon_startup(void)
info->var.bits_per_pixel);
fbcon_add_cursor_timer(info);
fbcon_has_exited = 0;
return display_desc;
}
static void fbcon_init(struct vc_data *vc, int init)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct fb_info *info;
struct fbcon_ops *ops;
struct vc_data **default_mode = vc->vc_display_fg;
struct vc_data *svc = *default_mode;
struct display *t, *p = &fb_display[vc->vc_num];
struct fbcon_display *t, *p = &fb_display[vc->vc_num];
int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
int cap, ret;
if (info_idx == -1 || info == NULL)
if (WARN_ON(info_idx == -1))
return;
if (con2fb_map[vc->vc_num] == -1)
con2fb_map[vc->vc_num] = info_idx;
info = registered_fb[con2fb_map[vc->vc_num]];
cap = info->flags;
if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
@ -1203,7 +1225,7 @@ static void fbcon_init(struct vc_data *vc, int init)
ops->p = &fb_display[fg_console];
}
static void fbcon_free_font(struct display *p, bool freefont)
static void fbcon_free_font(struct fbcon_display *p, bool freefont)
{
if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
@ -1215,7 +1237,7 @@ static void set_vc_hi_font(struct vc_data *vc, bool set);
static void fbcon_deinit(struct vc_data *vc)
{
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
struct fb_info *info;
struct fbcon_ops *ops;
int idx;
@ -1288,7 +1310,7 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct fbcon_ops *ops = info->fbcon_par;
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
u_int y_break;
if (fbcon_is_inactive(vc, info))
@ -1324,7 +1346,7 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
struct fbcon_ops *ops = info->fbcon_par;
if (!fbcon_is_inactive(vc, info))
@ -1388,7 +1410,7 @@ static int scrollback_current = 0;
static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
int unit)
{
struct display *p, *t;
struct fbcon_display *p, *t;
struct vc_data **default_mode, *vc;
struct vc_data *svc;
struct fbcon_ops *ops = info->fbcon_par;
@ -1457,7 +1479,7 @@ static __inline__ void ywrap_up(struct vc_data *vc, int count)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct fbcon_ops *ops = info->fbcon_par;
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
p->yscroll += count;
if (p->yscroll >= p->vrows) /* Deal with wrap */
@ -1476,7 +1498,7 @@ static __inline__ void ywrap_down(struct vc_data *vc, int count)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct fbcon_ops *ops = info->fbcon_par;
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
p->yscroll -= count;
if (p->yscroll < 0) /* Deal with wrap */
@ -1494,7 +1516,7 @@ static __inline__ void ywrap_down(struct vc_data *vc, int count)
static __inline__ void ypan_up(struct vc_data *vc, int count)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
struct fbcon_ops *ops = info->fbcon_par;
p->yscroll += count;
@ -1519,7 +1541,7 @@ static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct fbcon_ops *ops = info->fbcon_par;
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
p->yscroll += count;
@ -1542,7 +1564,7 @@ static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
static __inline__ void ypan_down(struct vc_data *vc, int count)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
struct fbcon_ops *ops = info->fbcon_par;
p->yscroll -= count;
@ -1567,7 +1589,7 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct fbcon_ops *ops = info->fbcon_par;
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
p->yscroll -= count;
@ -1587,7 +1609,7 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
scrollback_current = 0;
}
static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
static void fbcon_redraw_softback(struct vc_data *vc, struct fbcon_display *p,
long delta)
{
int count = vc->vc_rows;
@ -1680,7 +1702,7 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
}
}
static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
int line, int count, int dy)
{
unsigned short *s = (unsigned short *)
@ -1715,7 +1737,7 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
}
static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
struct display *p, int line, int count, int ycount)
struct fbcon_display *p, int line, int count, int ycount)
{
int offset = ycount * vc->vc_cols;
unsigned short *d = (unsigned short *)
@ -1764,7 +1786,7 @@ static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
}
}
static void fbcon_redraw(struct vc_data *vc, struct display *p,
static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p,
int line, int count, int offset)
{
unsigned short *d = (unsigned short *)
@ -1848,7 +1870,7 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
enum con_scroll dir, unsigned int count)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
if (fbcon_is_inactive(vc, info))
@ -2052,7 +2074,7 @@ static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
int height, int width)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
if (fbcon_is_inactive(vc, info))
return;
@ -2071,7 +2093,7 @@ static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
p->vrows - p->yscroll);
}
static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
int dy, int dx, int height, int width, u_int y_break)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
@ -2113,7 +2135,7 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s
height, width);
}
static void updatescrollmode(struct display *p,
static void updatescrollmode(struct fbcon_display *p,
struct fb_info *info,
struct vc_data *vc)
{
@ -2165,7 +2187,7 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct fbcon_ops *ops = info->fbcon_par;
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
struct fb_var_screeninfo var = info->var;
int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
@ -2210,7 +2232,7 @@ static int fbcon_switch(struct vc_data *vc)
{
struct fb_info *info, *old_info = NULL;
struct fbcon_ops *ops;
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
struct fb_var_screeninfo var;
int i, ret, prev_console, charcnt = 256;
@ -2348,8 +2370,6 @@ static int fbcon_switch(struct vc_data *vc)
static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
int blank)
{
struct fb_event event;
if (blank) {
unsigned short charmask = vc->vc_hi_font_mask ?
0x1ff : 0xff;
@ -2360,14 +2380,6 @@ static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
vc->vc_video_erase_char = oldc;
}
if (!lock_fb_info(info))
return;
event.info = info;
event.data = &blank;
fb_notifier_call_chain(FB_EVENT_CONBLANK, &event);
unlock_fb_info(info);
}
static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
@ -2394,9 +2406,8 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
ops->cursor_flash = (!blank);
if (!(info->flags & FBINFO_MISC_USEREVENT))
if (fb_blank(info, blank))
fbcon_generic_blank(vc, info, blank);
if (fb_blank(info, blank))
fbcon_generic_blank(vc, info, blank);
}
if (!blank)
@ -2553,7 +2564,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
struct fbcon_ops *ops = info->fbcon_par;
struct display *p = &fb_display[vc->vc_num];
struct fbcon_display *p = &fb_display[vc->vc_num];
int resize;
int cnt;
char *old_data = NULL;
@ -2601,7 +2612,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
static int fbcon_copy_font(struct vc_data *vc, int con)
{
struct display *od = &fb_display[con];
struct fbcon_display *od = &fb_display[con];
struct console_font *f = &vc->vc_font;
if (od->fontdata == f->data)
@ -2826,7 +2837,7 @@ static void fbcon_scrolldelta(struct vc_data *vc, int lines)
{
struct fb_info *info = registered_fb[con2fb_map[fg_console]];
struct fbcon_ops *ops = info->fbcon_par;
struct display *disp = &fb_display[fg_console];
struct fbcon_display *disp = &fb_display[fg_console];
int offset, limit, scrollback_old;
if (softback_top) {
@ -2918,7 +2929,7 @@ static int fbcon_set_origin(struct vc_data *vc)
return 0;
}
static void fbcon_suspended(struct fb_info *info)
void fbcon_suspended(struct fb_info *info)
{
struct vc_data *vc = NULL;
struct fbcon_ops *ops = info->fbcon_par;
@ -2931,7 +2942,7 @@ static void fbcon_suspended(struct fb_info *info)
fbcon_cursor(vc, CM_ERASE);
}
static void fbcon_resumed(struct fb_info *info)
void fbcon_resumed(struct fb_info *info)
{
struct vc_data *vc;
struct fbcon_ops *ops = info->fbcon_par;
@ -2947,7 +2958,7 @@ static void fbcon_modechanged(struct fb_info *info)
{
struct fbcon_ops *ops = info->fbcon_par;
struct vc_data *vc;
struct display *p;
struct fbcon_display *p;
int rows, cols;
if (!ops || ops->currcon < 0)
@ -2987,7 +2998,7 @@ static void fbcon_set_all_vcs(struct fb_info *info)
{
struct fbcon_ops *ops = info->fbcon_par;
struct vc_data *vc;
struct display *p;
struct fbcon_display *p;
int i, rows, cols, fg = -1;
if (!ops || ops->currcon < 0)
@ -3018,11 +3029,21 @@ static void fbcon_set_all_vcs(struct fb_info *info)
fbcon_modechanged(info);
}
static int fbcon_mode_deleted(struct fb_info *info,
struct fb_videomode *mode)
void fbcon_update_vcs(struct fb_info *info, bool all)
{
if (all)
fbcon_set_all_vcs(info);
else
fbcon_modechanged(info);
}
EXPORT_SYMBOL(fbcon_update_vcs);
int fbcon_mode_deleted(struct fb_info *info,
struct fb_videomode *mode)
{
struct fb_info *fb_info;
struct display *p;
struct fbcon_display *p;
int i, j, found = 0;
/* before deletion, ensure that mode is not in use */
@ -3045,7 +3066,7 @@ static int fbcon_mode_deleted(struct fb_info *info,
}
#ifdef CONFIG_VT_HW_CONSOLE_BINDING
static int fbcon_unbind(void)
static void fbcon_unbind(void)
{
int ret;
@ -3054,25 +3075,21 @@ static int fbcon_unbind(void)
if (!ret)
fbcon_has_console_bind = 0;
return ret;
}
#else
static inline int fbcon_unbind(void)
{
return -EINVAL;
}
static inline void fbcon_unbind(void) {}
#endif /* CONFIG_VT_HW_CONSOLE_BINDING */
/* called with console_lock held */
static int fbcon_fb_unbind(int idx)
void fbcon_fb_unbind(struct fb_info *info)
{
int i, new_idx = -1, ret = 0;
int idx = info->node;
WARN_CONSOLE_UNLOCKED();
if (!fbcon_has_console_bind)
return 0;
return;
for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map[i] != idx &&
@ -3105,26 +3122,24 @@ static int fbcon_fb_unbind(int idx)
idx, 0);
if (ret) {
con2fb_map[i] = idx;
return ret;
return;
}
}
}
}
ret = fbcon_unbind();
fbcon_unbind();
}
return ret;
}
/* called with console_lock held */
static int fbcon_fb_unregistered(struct fb_info *info)
void fbcon_fb_unregistered(struct fb_info *info)
{
int i, idx;
WARN_CONSOLE_UNLOCKED();
if (deferred_takeover)
return 0;
return;
idx = info->node;
for (i = first_fb_vc; i <= last_fb_vc; i++) {
@ -3153,21 +3168,18 @@ static int fbcon_fb_unregistered(struct fb_info *info)
if (!num_registered_fb)
do_unregister_con_driver(&fb_con);
return 0;
}
/* called with console_lock held */
static void fbcon_remap_all(int idx)
void fbcon_remap_all(struct fb_info *info)
{
int i;
WARN_CONSOLE_UNLOCKED();
int i, idx = info->node;
console_lock();
if (deferred_takeover) {
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map_boot[i] = idx;
fbcon_map_override();
console_unlock();
return;
}
@ -3180,6 +3192,7 @@ static void fbcon_remap_all(int idx)
first_fb_vc + 1, last_fb_vc + 1);
info_idx = idx;
}
console_unlock();
}
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
@ -3213,7 +3226,7 @@ static inline void fbcon_select_primary(struct fb_info *info)
#endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
/* called with console_lock held */
static int fbcon_fb_registered(struct fb_info *info)
int fbcon_fb_registered(struct fb_info *info)
{
int ret = 0, i, idx;
@ -3247,7 +3260,7 @@ static int fbcon_fb_registered(struct fb_info *info)
return ret;
}
static void fbcon_fb_blanked(struct fb_info *info, int blank)
void fbcon_fb_blanked(struct fb_info *info, int blank)
{
struct fbcon_ops *ops = info->fbcon_par;
struct vc_data *vc;
@ -3269,7 +3282,7 @@ static void fbcon_fb_blanked(struct fb_info *info, int blank)
ops->blank_state = blank;
}
static void fbcon_new_modelist(struct fb_info *info)
void fbcon_new_modelist(struct fb_info *info)
{
int i;
struct vc_data *vc;
@ -3290,11 +3303,11 @@ static void fbcon_new_modelist(struct fb_info *info)
}
}
static void fbcon_get_requirement(struct fb_info *info,
struct fb_blit_caps *caps)
void fbcon_get_requirement(struct fb_info *info,
struct fb_blit_caps *caps)
{
struct vc_data *vc;
struct display *p;
struct fbcon_display *p;
if (caps->flags) {
int i, charcnt;
@ -3326,80 +3339,47 @@ static void fbcon_get_requirement(struct fb_info *info,
}
}
static int fbcon_event_notify(struct notifier_block *self,
unsigned long action, void *data)
int fbcon_set_con2fb_map_ioctl(void __user *argp)
{
struct fb_event *event = data;
struct fb_info *info = event->info;
struct fb_videomode *mode;
struct fb_con2fbmap *con2fb;
struct fb_blit_caps *caps;
int idx, ret = 0;
struct fb_con2fbmap con2fb;
int ret;
/*
* ignore all events except driver registration and deregistration
* if fbcon is not active
*/
if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
action == FB_EVENT_FB_UNREGISTERED))
goto done;
switch(action) {
case FB_EVENT_SUSPEND:
fbcon_suspended(info);
break;
case FB_EVENT_RESUME:
fbcon_resumed(info);
break;
case FB_EVENT_MODE_CHANGE:
fbcon_modechanged(info);
break;
case FB_EVENT_MODE_CHANGE_ALL:
fbcon_set_all_vcs(info);
break;
case FB_EVENT_MODE_DELETE:
mode = event->data;
ret = fbcon_mode_deleted(info, mode);
break;
case FB_EVENT_FB_UNBIND:
idx = info->node;
ret = fbcon_fb_unbind(idx);
break;
case FB_EVENT_FB_REGISTERED:
ret = fbcon_fb_registered(info);
break;
case FB_EVENT_FB_UNREGISTERED:
ret = fbcon_fb_unregistered(info);
break;
case FB_EVENT_SET_CONSOLE_MAP:
/* called with console lock held */
con2fb = event->data;
ret = set_con2fb_map(con2fb->console - 1,
con2fb->framebuffer, 1);
break;
case FB_EVENT_GET_CONSOLE_MAP:
con2fb = event->data;
con2fb->framebuffer = con2fb_map[con2fb->console - 1];
break;
case FB_EVENT_BLANK:
fbcon_fb_blanked(info, *(int *)event->data);
break;
case FB_EVENT_NEW_MODELIST:
fbcon_new_modelist(info);
break;
case FB_EVENT_GET_REQ:
caps = event->data;
fbcon_get_requirement(info, caps);
break;
case FB_EVENT_REMAP_ALL_CONSOLE:
idx = info->node;
fbcon_remap_all(idx);
break;
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
return -EFAULT;
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
if (con2fb.framebuffer >= FB_MAX)
return -EINVAL;
if (!registered_fb[con2fb.framebuffer])
request_module("fb%d", con2fb.framebuffer);
if (!registered_fb[con2fb.framebuffer]) {
return -EINVAL;
}
done:
console_lock();
ret = set_con2fb_map(con2fb.console - 1,
con2fb.framebuffer, 1);
console_unlock();
return ret;
}
int fbcon_get_con2fb_map_ioctl(void __user *argp)
{
struct fb_con2fbmap con2fb;
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
return -EFAULT;
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
console_lock();
con2fb.framebuffer = con2fb_map[con2fb.console - 1];
console_unlock();
return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
}
/*
* The console `switch' structure for the frame buffer based console
*/
@ -3431,10 +3411,6 @@ static const struct consw fb_con = {
.con_debug_leave = fbcon_debug_leave,
};
static struct notifier_block fbcon_event_notifier = {
.notifier_call = fbcon_event_notify,
};
static ssize_t store_rotate(struct device *device,
struct device_attribute *attr, const char *buf,
size_t count)
@ -3443,9 +3419,6 @@ static ssize_t store_rotate(struct device *device,
int rotate, idx;
char **last = NULL;
if (fbcon_has_exited)
return count;
console_lock();
idx = con2fb_map[fg_console];
@ -3468,9 +3441,6 @@ static ssize_t store_rotate_all(struct device *device,
int rotate, idx;
char **last = NULL;
if (fbcon_has_exited)
return count;
console_lock();
idx = con2fb_map[fg_console];
@ -3491,9 +3461,6 @@ static ssize_t show_rotate(struct device *device,
struct fb_info *info;
int rotate = 0, idx;
if (fbcon_has_exited)
return 0;
console_lock();
idx = con2fb_map[fg_console];
@ -3514,9 +3481,6 @@ static ssize_t show_cursor_blink(struct device *device,
struct fbcon_ops *ops;
int idx, blink = -1;
if (fbcon_has_exited)
return 0;
console_lock();
idx = con2fb_map[fg_console];
@ -3543,9 +3507,6 @@ static ssize_t store_cursor_blink(struct device *device,
int blink, idx;
char **last = NULL;
if (fbcon_has_exited)
return count;
console_lock();
idx = con2fb_map[fg_console];
@ -3668,9 +3629,6 @@ static void fbcon_exit(void)
struct fb_info *info;
int i, j, mapped;
if (fbcon_has_exited)
return;
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
if (deferred_takeover) {
dummycon_unregister_output_notifier(&fbcon_output_nb);
@ -3695,7 +3653,7 @@ static void fbcon_exit(void)
for (j = first_fb_vc; j <= last_fb_vc; j++) {
if (con2fb_map[j] == i) {
mapped = 1;
break;
con2fb_map[j] = -1;
}
}
@ -3718,8 +3676,6 @@ static void fbcon_exit(void)
info->queue.func = NULL;
}
}
fbcon_has_exited = 1;
}
void __init fb_console_init(void)
@ -3727,7 +3683,6 @@ void __init fb_console_init(void)
int i;
console_lock();
fb_register_client(&fbcon_event_notifier);
fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
"fbcon");
@ -3763,7 +3718,6 @@ static void __exit fbcon_deinit_device(void)
void __exit fb_console_exit(void)
{
console_lock();
fb_unregister_client(&fbcon_event_notifier);
fbcon_deinit_device();
device_destroy(fb_class, MKDEV(0, 0));
fbcon_exit();

View File

@ -25,7 +25,7 @@
* low-level frame buffer device
*/
struct display {
struct fbcon_display {
/* Filled in by the low-level console driver */
const u_char *fontdata;
int userfont; /* != 0 if fontdata kmalloc()ed */
@ -68,7 +68,7 @@ struct fbcon_ops {
struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */
struct timer_list cursor_timer; /* Cursor timer */
struct fb_cursor cursor_state;
struct display *p;
struct fbcon_display *p;
struct fb_info *info;
int currcon; /* Current VC. */
int cur_blink_jiffies;
@ -225,7 +225,7 @@ extern int soft_cursor(struct fb_info *info, struct fb_cursor *cursor);
#define FBCON_ATTRIBUTE_REVERSE 2
#define FBCON_ATTRIBUTE_BOLD 4
static inline int real_y(struct display *p, int ypos)
static inline int real_y(struct fbcon_display *p, int ypos)
{
int rows = p->vrows;

View File

@ -80,17 +80,6 @@ static void put_fb_info(struct fb_info *fb_info)
fb_info->fbops->fb_destroy(fb_info);
}
int lock_fb_info(struct fb_info *info)
{
mutex_lock(&info->lock);
if (!info->fbops) {
mutex_unlock(&info->lock);
return 0;
}
return 1;
}
EXPORT_SYMBOL(lock_fb_info);
/*
* Helpers
*/
@ -943,16 +932,13 @@ EXPORT_SYMBOL(fb_pan_display);
static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
u32 activate)
{
struct fb_event event;
struct fb_blit_caps caps, fbcaps;
int err = 0;
memset(&caps, 0, sizeof(caps));
memset(&fbcaps, 0, sizeof(fbcaps));
caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
event.info = info;
event.data = &caps;
fb_notifier_call_chain(FB_EVENT_GET_REQ, &event);
fbcon_get_requirement(info, &caps);
info->fbops->fb_get_caps(info, &fbcaps, var);
if (((fbcaps.x ^ caps.x) & caps.x) ||
@ -968,6 +954,10 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
{
int flags = info->flags;
int ret = 0;
u32 activate;
struct fb_var_screeninfo old_var;
struct fb_videomode mode;
struct fb_event event;
if (var->activate & FB_ACTIVATE_INV_MODE) {
struct fb_videomode mode1, mode2;
@ -977,100 +967,90 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
/* make sure we don't delete the videomode of current var */
ret = fb_mode_is_equal(&mode1, &mode2);
if (!ret) {
struct fb_event event;
event.info = info;
event.data = &mode1;
ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event);
}
if (!ret)
fbcon_mode_deleted(info, &mode1);
if (!ret)
fb_delete_videomode(&mode1, &info->modelist);
fb_delete_videomode(&mode1, &info->modelist);
ret = (ret) ? -EINVAL : 0;
goto done;
return ret ? -EINVAL : 0;
}
if ((var->activate & FB_ACTIVATE_FORCE) ||
memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {
u32 activate = var->activate;
if (!(var->activate & FB_ACTIVATE_FORCE) &&
!memcmp(&info->var, var, sizeof(struct fb_var_screeninfo)))
return 0;
/* When using FOURCC mode, make sure the red, green, blue and
* transp fields are set to 0.
*/
if ((info->fix.capabilities & FB_CAP_FOURCC) &&
var->grayscale > 1) {
if (var->red.offset || var->green.offset ||
var->blue.offset || var->transp.offset ||
var->red.length || var->green.length ||
var->blue.length || var->transp.length ||
var->red.msb_right || var->green.msb_right ||
var->blue.msb_right || var->transp.msb_right)
return -EINVAL;
}
activate = var->activate;
if (!info->fbops->fb_check_var) {
*var = info->var;
goto done;
}
/* When using FOURCC mode, make sure the red, green, blue and
* transp fields are set to 0.
*/
if ((info->fix.capabilities & FB_CAP_FOURCC) &&
var->grayscale > 1) {
if (var->red.offset || var->green.offset ||
var->blue.offset || var->transp.offset ||
var->red.length || var->green.length ||
var->blue.length || var->transp.length ||
var->red.msb_right || var->green.msb_right ||
var->blue.msb_right || var->transp.msb_right)
return -EINVAL;
}
ret = info->fbops->fb_check_var(var, info);
if (!info->fbops->fb_check_var) {
*var = info->var;
return 0;
}
ret = info->fbops->fb_check_var(var, info);
if (ret)
return ret;
if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
return 0;
if (info->fbops->fb_get_caps) {
ret = fb_check_caps(info, var, activate);
if (ret)
goto done;
return ret;
}
if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
struct fb_var_screeninfo old_var;
struct fb_videomode mode;
old_var = info->var;
info->var = *var;
if (info->fbops->fb_get_caps) {
ret = fb_check_caps(info, var, activate);
if (info->fbops->fb_set_par) {
ret = info->fbops->fb_set_par(info);
if (ret)
goto done;
}
old_var = info->var;
info->var = *var;
if (info->fbops->fb_set_par) {
ret = info->fbops->fb_set_par(info);
if (ret) {
info->var = old_var;
printk(KERN_WARNING "detected "
"fb_set_par error, "
"error code: %d\n", ret);
goto done;
}
}
fb_pan_display(info, &info->var);
fb_set_cmap(&info->cmap, info);
fb_var_to_videomode(&mode, &info->var);
if (info->modelist.prev && info->modelist.next &&
!list_empty(&info->modelist))
ret = fb_add_videomode(&mode, &info->modelist);
if (!ret && (flags & FBINFO_MISC_USEREVENT)) {
struct fb_event event;
int evnt = (activate & FB_ACTIVATE_ALL) ?
FB_EVENT_MODE_CHANGE_ALL :
FB_EVENT_MODE_CHANGE;
info->flags &= ~FBINFO_MISC_USEREVENT;
event.info = info;
event.data = &mode;
fb_notifier_call_chain(evnt, &event);
}
if (ret) {
info->var = old_var;
printk(KERN_WARNING "detected "
"fb_set_par error, "
"error code: %d\n", ret);
return ret;
}
}
done:
return ret;
fb_pan_display(info, &info->var);
fb_set_cmap(&info->cmap, info);
fb_var_to_videomode(&mode, &info->var);
if (info->modelist.prev && info->modelist.next &&
!list_empty(&info->modelist))
ret = fb_add_videomode(&mode, &info->modelist);
if (ret)
return ret;
event.info = info;
event.data = &mode;
fb_notifier_call_chain(FB_EVENT_MODE_CHANGE, &event);
if (flags & FBINFO_MISC_USEREVENT)
fbcon_update_vcs(info, activate & FB_ACTIVATE_ALL);
return 0;
}
EXPORT_SYMBOL(fb_set_var);
@ -1112,17 +1092,14 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
struct fb_ops *fb;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
struct fb_con2fbmap con2fb;
struct fb_cmap cmap_from;
struct fb_cmap_user cmap;
struct fb_event event;
void __user *argp = (void __user *)arg;
long ret = 0;
switch (cmd) {
case FBIOGET_VSCREENINFO:
if (!lock_fb_info(info))
return -ENODEV;
lock_fb_info(info);
var = info->var;
unlock_fb_info(info);
@ -1132,10 +1109,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
if (copy_from_user(&var, argp, sizeof(var)))
return -EFAULT;
console_lock();
if (!lock_fb_info(info)) {
console_unlock();
return -ENODEV;
}
lock_fb_info(info);
info->flags |= FBINFO_MISC_USEREVENT;
ret = fb_set_var(info, &var);
info->flags &= ~FBINFO_MISC_USEREVENT;
@ -1145,8 +1119,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
ret = -EFAULT;
break;
case FBIOGET_FSCREENINFO:
if (!lock_fb_info(info))
return -ENODEV;
lock_fb_info(info);
fix = info->fix;
if (info->flags & FBINFO_HIDE_SMEM_START)
fix.smem_start = 0;
@ -1162,8 +1135,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
case FBIOGETCMAP:
if (copy_from_user(&cmap, argp, sizeof(cmap)))
return -EFAULT;
if (!lock_fb_info(info))
return -ENODEV;
lock_fb_info(info);
cmap_from = info->cmap;
unlock_fb_info(info);
ret = fb_cmap_to_user(&cmap_from, &cmap);
@ -1172,10 +1144,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
if (copy_from_user(&var, argp, sizeof(var)))
return -EFAULT;
console_lock();
if (!lock_fb_info(info)) {
console_unlock();
return -ENODEV;
}
lock_fb_info(info);
ret = fb_pan_display(info, &var);
unlock_fb_info(info);
console_unlock();
@ -1186,58 +1155,22 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
ret = -EINVAL;
break;
case FBIOGET_CON2FBMAP:
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
return -EFAULT;
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
con2fb.framebuffer = -1;
event.data = &con2fb;
if (!lock_fb_info(info))
return -ENODEV;
event.info = info;
fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
unlock_fb_info(info);
ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
ret = fbcon_get_con2fb_map_ioctl(argp);
break;
case FBIOPUT_CON2FBMAP:
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
return -EFAULT;
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
if (con2fb.framebuffer >= FB_MAX)
return -EINVAL;
if (!registered_fb[con2fb.framebuffer])
request_module("fb%d", con2fb.framebuffer);
if (!registered_fb[con2fb.framebuffer]) {
ret = -EINVAL;
break;
}
event.data = &con2fb;
console_lock();
if (!lock_fb_info(info)) {
console_unlock();
return -ENODEV;
}
event.info = info;
ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
unlock_fb_info(info);
console_unlock();
ret = fbcon_set_con2fb_map_ioctl(argp);
break;
case FBIOBLANK:
console_lock();
if (!lock_fb_info(info)) {
console_unlock();
return -ENODEV;
}
info->flags |= FBINFO_MISC_USEREVENT;
lock_fb_info(info);
ret = fb_blank(info, arg);
info->flags &= ~FBINFO_MISC_USEREVENT;
/* might again call into fb_blank */
fbcon_fb_blanked(info, arg);
unlock_fb_info(info);
console_unlock();
break;
default:
if (!lock_fb_info(info))
return -ENODEV;
lock_fb_info(info);
fb = info->fbops;
if (fb->fb_ioctl)
ret = fb->fb_ioctl(info, cmd, arg);
@ -1357,8 +1290,7 @@ static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
{
struct fb_fix_screeninfo fix;
if (!lock_fb_info(info))
return -ENODEV;
lock_fb_info(info);
fix = info->fix;
if (info->flags & FBINFO_HIDE_SMEM_START)
fix.smem_start = 0;
@ -1418,8 +1350,6 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
if (!info)
return -ENODEV;
fb = info->fbops;
if (!fb)
return -ENODEV;
mutex_lock(&info->mm_lock);
if (fb->fb_mmap) {
int res;
@ -1483,7 +1413,7 @@ __releases(&info->lock)
if (IS_ERR(info))
return PTR_ERR(info);
mutex_lock(&info->lock);
lock_fb_info(info);
if (!try_module_get(info->fbops->owner)) {
res = -ENODEV;
goto out;
@ -1499,7 +1429,7 @@ __releases(&info->lock)
fb_deferred_io_open(info, inode, file);
#endif
out:
mutex_unlock(&info->lock);
unlock_fb_info(info);
if (res)
put_fb_info(info);
return res;
@ -1512,11 +1442,11 @@ __releases(&info->lock)
{
struct fb_info * const info = file->private_data;
mutex_lock(&info->lock);
lock_fb_info(info);
if (info->fbops->fb_release)
info->fbops->fb_release(info,1);
module_put(info->fbops->owner);
mutex_unlock(&info->lock);
unlock_fb_info(info);
put_fb_info(info);
return 0;
}
@ -1621,13 +1551,13 @@ static bool fb_do_apertures_overlap(struct apertures_struct *gena,
return false;
}
static int do_unregister_framebuffer(struct fb_info *fb_info);
static void do_unregister_framebuffer(struct fb_info *fb_info);
#define VGA_FB_PHYS 0xA0000
static int do_remove_conflicting_framebuffers(struct apertures_struct *a,
const char *name, bool primary)
static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
const char *name, bool primary)
{
int i, ret;
int i;
/* check all firmware fbs and kick off if the base addr overlaps */
for_each_registered_fb(i) {
@ -1643,13 +1573,9 @@ static int do_remove_conflicting_framebuffers(struct apertures_struct *a,
printk(KERN_INFO "fb%d: switching to %s from %s\n",
i, name, registered_fb[i]->fix.id);
ret = do_unregister_framebuffer(registered_fb[i]);
if (ret)
return ret;
do_unregister_framebuffer(registered_fb[i]);
}
}
return 0;
}
static bool lockless_register_fb;
@ -1660,17 +1586,14 @@ MODULE_PARM_DESC(lockless_register_fb,
static int do_register_framebuffer(struct fb_info *fb_info)
{
int i, ret;
struct fb_event event;
struct fb_videomode mode;
if (fb_check_foreignness(fb_info))
return -ENOSYS;
ret = do_remove_conflicting_framebuffers(fb_info->apertures,
fb_info->fix.id,
fb_is_primary_device(fb_info));
if (ret)
return ret;
do_remove_conflicting_framebuffers(fb_info->apertures,
fb_info->fix.id,
fb_is_primary_device(fb_info));
if (num_registered_fb == FB_MAX)
return -ENXIO;
@ -1723,20 +1646,22 @@ static int do_register_framebuffer(struct fb_info *fb_info)
fb_add_videomode(&mode, &fb_info->modelist);
registered_fb[i] = fb_info;
event.info = fb_info;
#ifdef CONFIG_GUMSTIX_AM200EPD
{
struct fb_event event;
event.info = fb_info;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
}
#endif
if (!lockless_register_fb)
console_lock();
else
atomic_inc(&ignore_console_lock_warning);
if (!lock_fb_info(fb_info)) {
ret = -ENODEV;
goto unlock_console;
}
ret = 0;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
lock_fb_info(fb_info);
ret = fbcon_fb_registered(fb_info);
unlock_fb_info(fb_info);
unlock_console:
if (!lockless_register_fb)
console_unlock();
else
@ -1744,44 +1669,44 @@ unlock_console:
return ret;
}
static int unbind_console(struct fb_info *fb_info)
static void unbind_console(struct fb_info *fb_info)
{
struct fb_event event;
int ret;
int i = fb_info->node;
if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
return -EINVAL;
if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
return;
console_lock();
if (!lock_fb_info(fb_info)) {
console_unlock();
return -ENODEV;
}
event.info = fb_info;
ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
lock_fb_info(fb_info);
fbcon_fb_unbind(fb_info);
unlock_fb_info(fb_info);
console_unlock();
return ret;
}
static int __unlink_framebuffer(struct fb_info *fb_info);
static int do_unregister_framebuffer(struct fb_info *fb_info)
void unlink_framebuffer(struct fb_info *fb_info)
{
struct fb_event event;
int ret;
int i;
ret = unbind_console(fb_info);
i = fb_info->node;
if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
return;
if (ret)
return -EINVAL;
if (!fb_info->dev)
return;
device_destroy(fb_class, MKDEV(FB_MAJOR, i));
pm_vt_switch_unregister(fb_info->dev);
__unlink_framebuffer(fb_info);
unbind_console(fb_info);
fb_info->dev = NULL;
}
EXPORT_SYMBOL(unlink_framebuffer);
static void do_unregister_framebuffer(struct fb_info *fb_info)
{
unlink_framebuffer(fb_info);
if (fb_info->pixmap.addr &&
(fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
kfree(fb_info->pixmap.addr);
@ -1789,46 +1714,21 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
registered_fb[fb_info->node] = NULL;
num_registered_fb--;
fb_cleanup_device(fb_info);
event.info = fb_info;
#ifdef CONFIG_GUMSTIX_AM200EPD
{
struct fb_event event;
event.info = fb_info;
fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
}
#endif
console_lock();
fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
fbcon_fb_unregistered(fb_info);
console_unlock();
/* this may free fb info */
put_fb_info(fb_info);
return 0;
}
static int __unlink_framebuffer(struct fb_info *fb_info)
{
int i;
i = fb_info->node;
if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
return -EINVAL;
if (fb_info->dev) {
device_destroy(fb_class, MKDEV(FB_MAJOR, i));
fb_info->dev = NULL;
}
return 0;
}
int unlink_framebuffer(struct fb_info *fb_info)
{
int ret;
ret = __unlink_framebuffer(fb_info);
if (ret)
return ret;
unbind_console(fb_info);
return 0;
}
EXPORT_SYMBOL(unlink_framebuffer);
/**
* remove_conflicting_framebuffers - remove firmware-configured framebuffers
* @a: memory range, users of which are to be removed
@ -1842,7 +1742,6 @@ EXPORT_SYMBOL(unlink_framebuffer);
int remove_conflicting_framebuffers(struct apertures_struct *a,
const char *name, bool primary)
{
int ret;
bool do_free = false;
if (!a) {
@ -1856,13 +1755,13 @@ int remove_conflicting_framebuffers(struct apertures_struct *a,
}
mutex_lock(&registration_lock);
ret = do_remove_conflicting_framebuffers(a, name, primary);
do_remove_conflicting_framebuffers(a, name, primary);
mutex_unlock(&registration_lock);
if (do_free)
kfree(a);
return ret;
return 0;
}
EXPORT_SYMBOL(remove_conflicting_framebuffers);
@ -1959,16 +1858,12 @@ EXPORT_SYMBOL(register_framebuffer);
* that the driver implements fb_open() and fb_release() to
* check that no processes are using the device.
*/
int
void
unregister_framebuffer(struct fb_info *fb_info)
{
int ret;
mutex_lock(&registration_lock);
ret = do_unregister_framebuffer(fb_info);
do_unregister_framebuffer(fb_info);
mutex_unlock(&registration_lock);
return ret;
}
EXPORT_SYMBOL(unregister_framebuffer);
@ -1983,15 +1878,14 @@ EXPORT_SYMBOL(unregister_framebuffer);
*/
void fb_set_suspend(struct fb_info *info, int state)
{
struct fb_event event;
WARN_CONSOLE_UNLOCKED();
event.info = info;
if (state) {
fb_notifier_call_chain(FB_EVENT_SUSPEND, &event);
fbcon_suspended(info);
info->state = FBINFO_STATE_SUSPENDED;
} else {
info->state = FBINFO_STATE_RUNNING;
fb_notifier_call_chain(FB_EVENT_RESUME, &event);
fbcon_resumed(info);
}
}
EXPORT_SYMBOL(fb_set_suspend);
@ -2059,7 +1953,6 @@ subsys_initcall(fbmem_init);
int fb_new_modelist(struct fb_info *info)
{
struct fb_event event;
struct fb_var_screeninfo var = info->var;
struct list_head *pos, *n;
struct fb_modelist *modelist;
@ -2079,14 +1972,12 @@ int fb_new_modelist(struct fb_info *info)
}
}
err = 1;
if (list_empty(&info->modelist))
return 1;
if (!list_empty(&info->modelist)) {
event.info = info;
err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
}
fbcon_new_modelist(info);
return err;
return 0;
}
MODULE_LICENSE("GPL");

View File

@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fb.h>
#include <linux/fbcon.h>
#include <linux/console.h>
#include <linux/module.h>
@ -175,10 +176,7 @@ static ssize_t store_modes(struct device *device,
return -EINVAL;
console_lock();
if (!lock_fb_info(fb_info)) {
console_unlock();
return -ENODEV;
}
lock_fb_info(fb_info);
list_splice(&fb_info->modelist, &old_list);
fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
@ -304,12 +302,13 @@ static ssize_t store_blank(struct device *device,
{
struct fb_info *fb_info = dev_get_drvdata(device);
char *last = NULL;
int err;
int err, arg;
arg = simple_strtoul(buf, &last, 0);
console_lock();
fb_info->flags |= FBINFO_MISC_USEREVENT;
err = fb_blank(fb_info, simple_strtoul(buf, &last, 0));
fb_info->flags &= ~FBINFO_MISC_USEREVENT;
err = fb_blank(fb_info, arg);
/* might again call into fb_blank */
fbcon_fb_blanked(fb_info, arg);
console_unlock();
if (err < 0)
return err;
@ -405,10 +404,7 @@ static ssize_t store_fbstate(struct device *device,
state = simple_strtoul(buf, &last, 0);
console_lock();
if (!lock_fb_info(fb_info)) {
console_unlock();
return -ENODEV;
}
lock_fb_info(fb_info);
fb_set_suspend(fb_info, (int)state);

View File

@ -61,7 +61,6 @@
struct cfb_info {
struct fb_info fb;
struct display_switch *dispsw;
struct display *display;
unsigned char __iomem *region;
unsigned char __iomem *regs;
u_int id;

View File

@ -2122,14 +2122,7 @@ static void neofb_remove(struct pci_dev *dev)
DBG("neofb_remove");
if (info) {
/*
* If unregister_framebuffer fails, then
* we will be leaving hooks that could cause
* oopsen laying around.
*/
if (unregister_framebuffer(info))
printk(KERN_WARNING
"neofb: danger danger! Oopsen imminent!\n");
unregister_framebuffer(info);
neo_unmap_video(info);
fb_destroy_modedb(info->monspecs.modedb);

View File

@ -60,8 +60,7 @@ static ssize_t store_rotate_type(struct device *dev,
if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB)
return -EINVAL;
if (!lock_fb_info(fbi))
return -ENODEV;
lock_fb_info(fbi);
r = 0;
if (rot_type == ofbi->rotation_type)
@ -112,8 +111,7 @@ static ssize_t store_mirror(struct device *dev,
if (r)
return r;
if (!lock_fb_info(fbi))
return -ENODEV;
lock_fb_info(fbi);
ofbi->mirror = mirror;
@ -149,8 +147,7 @@ static ssize_t show_overlays(struct device *dev,
ssize_t l = 0;
int t;
if (!lock_fb_info(fbi))
return -ENODEV;
lock_fb_info(fbi);
omapfb_lock(fbdev);
for (t = 0; t < ofbi->num_overlays; t++) {
@ -208,8 +205,7 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr,
if (buf[len - 1] == '\n')
len = len - 1;
if (!lock_fb_info(fbi))
return -ENODEV;
lock_fb_info(fbi);
omapfb_lock(fbdev);
if (len > 0) {
@ -340,8 +336,7 @@ static ssize_t show_overlays_rotate(struct device *dev,
ssize_t l = 0;
int t;
if (!lock_fb_info(fbi))
return -ENODEV;
lock_fb_info(fbi);
for (t = 0; t < ofbi->num_overlays; t++) {
l += snprintf(buf + l, PAGE_SIZE - l, "%s%d",
@ -369,8 +364,7 @@ static ssize_t store_overlays_rotate(struct device *dev,
if (buf[len - 1] == '\n')
len = len - 1;
if (!lock_fb_info(fbi))
return -ENODEV;
lock_fb_info(fbi);
if (len > 0) {
char *p = (char *)buf;
@ -453,8 +447,7 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
size = PAGE_ALIGN(size);
if (!lock_fb_info(fbi))
return -ENODEV;
lock_fb_info(fbi);
if (display && display->driver->sync)
display->driver->sync(display);

View File

@ -974,35 +974,10 @@ static void sa1100fb_task(struct work_struct *w)
*/
static unsigned int sa1100fb_min_dma_period(struct sa1100fb_info *fbi)
{
#if 0
unsigned int min_period = (unsigned int)-1;
int i;
for (i = 0; i < MAX_NR_CONSOLES; i++) {
struct display *disp = &fb_display[i];
unsigned int period;
/*
* Do we own this display?
*/
if (disp->fb_info != &fbi->fb)
continue;
/*
* Ok, calculate its DMA period
*/
period = sa1100fb_display_dma_period(&disp->var);
if (period < min_period)
min_period = period;
}
return min_period;
#else
/*
* FIXME: we need to verify _all_ consoles.
*/
return sa1100fb_display_dma_period(&fbi->fb.var);
#endif
}
/*

View File

@ -2333,14 +2333,7 @@ static void savagefb_remove(struct pci_dev *dev)
DBG("savagefb_remove");
if (info) {
/*
* If unregister_framebuffer fails, then
* we will be leaving hooks that could cause
* oopsen laying around.
*/
if (unregister_framebuffer(info))
printk(KERN_WARNING "savagefb: danger danger! "
"Oopsen imminent!\n");
unregister_framebuffer(info);
#ifdef CONFIG_FB_SAVAGE_I2C
savagefb_delete_i2c_busses(info);

View File

@ -15,6 +15,7 @@
#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/fbcon.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@ -213,7 +214,6 @@ struct sh_mobile_lcdc_priv {
struct sh_mobile_lcdc_chan ch[2];
struct sh_mobile_lcdc_overlay overlays[4];
struct notifier_block notifier;
int started;
int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
};
@ -534,89 +534,9 @@ static void sh_mobile_lcdc_display_off(struct sh_mobile_lcdc_chan *ch)
ch->tx_dev->ops->display_off(ch->tx_dev);
}
static bool
sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch,
const struct fb_videomode *new_mode)
{
dev_dbg(ch->info->dev, "Old %ux%u, new %ux%u\n",
ch->display.mode.xres, ch->display.mode.yres,
new_mode->xres, new_mode->yres);
/* It can be a different monitor with an equal video-mode */
if (fb_mode_is_equal(&ch->display.mode, new_mode))
return false;
dev_dbg(ch->info->dev, "Switching %u -> %u lines\n",
ch->display.mode.yres, new_mode->yres);
ch->display.mode = *new_mode;
return true;
}
static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
struct fb_info *info);
static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
enum sh_mobile_lcdc_entity_event event,
const struct fb_videomode *mode,
const struct fb_monspecs *monspec)
{
struct fb_info *info = ch->info;
struct fb_var_screeninfo var;
int ret = 0;
switch (event) {
case SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT:
/* HDMI plug in */
console_lock();
if (lock_fb_info(info)) {
ch->display.width = monspec->max_x * 10;
ch->display.height = monspec->max_y * 10;
if (!sh_mobile_lcdc_must_reconfigure(ch, mode) &&
info->state == FBINFO_STATE_RUNNING) {
/* First activation with the default monitor.
* Just turn on, if we run a resume here, the
* logo disappears.
*/
info->var.width = ch->display.width;
info->var.height = ch->display.height;
sh_mobile_lcdc_display_on(ch);
} else {
/* New monitor or have to wake up */
fb_set_suspend(info, 0);
}
unlock_fb_info(info);
}
console_unlock();
break;
case SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT:
/* HDMI disconnect */
console_lock();
if (lock_fb_info(info)) {
fb_set_suspend(info, 1);
unlock_fb_info(info);
}
console_unlock();
break;
case SH_MOBILE_LCDC_EVENT_DISPLAY_MODE:
/* Validate a proposed new mode */
fb_videomode_to_var(&var, mode);
var.bits_per_pixel = info->var.bits_per_pixel;
var.grayscale = info->var.grayscale;
ret = sh_mobile_lcdc_check_var(&var, info);
break;
}
return ret;
}
/* -----------------------------------------------------------------------------
* Format helpers
*/
@ -1838,8 +1758,6 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
struct sh_mobile_lcdc_chan *ch = info->par;
struct fb_var_screeninfo var;
struct fb_videomode mode;
struct fb_event event;
int evnt = FB_EVENT_MODE_CHANGE_ALL;
if (ch->use_count > 1 || (ch->use_count == 1 && !info->fbcon_par))
/* More framebuffer users are active */
@ -1861,14 +1779,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
/* Couldn't reconfigure, hopefully, can continue as before */
return;
/*
* fb_set_var() calls the notifier change internally, only if
* FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a
* user event, we have to call the chain ourselves.
*/
event.info = info;
event.data = &ch->display.mode;
fb_notifier_call_chain(evnt, &event);
fbcon_update_vcs(info, true);
}
/*
@ -2319,37 +2230,6 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
* Framebuffer notifier
*/
/* locking: called with info->lock held */
static int sh_mobile_lcdc_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct fb_event *event = data;
struct fb_info *info = event->info;
struct sh_mobile_lcdc_chan *ch = info->par;
if (&ch->lcdc->notifier != nb)
return NOTIFY_DONE;
dev_dbg(info->dev, "%s(): action = %lu, data = %p\n",
__func__, action, event->data);
switch(action) {
case FB_EVENT_SUSPEND:
sh_mobile_lcdc_display_off(ch);
sh_mobile_lcdc_stop(ch->lcdc);
break;
case FB_EVENT_RESUME:
mutex_lock(&ch->open_lock);
sh_mobile_fb_reconfig(info);
mutex_unlock(&ch->open_lock);
sh_mobile_lcdc_display_on(ch);
sh_mobile_lcdc_start(ch->lcdc);
}
return NOTIFY_OK;
}
/* -----------------------------------------------------------------------------
* Probe/remove and driver init/exit
*/
@ -2377,8 +2257,6 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
unsigned int i;
fb_unregister_client(&priv->notifier);
for (i = 0; i < ARRAY_SIZE(priv->overlays); i++)
sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]);
for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
@ -2540,8 +2418,6 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch)
unsigned int max_size;
unsigned int i;
ch->notify = sh_mobile_lcdc_display_notify;
/* Validate the format. */
format = sh_mobile_format_info(cfg->fourcc);
if (format == NULL) {
@ -2770,10 +2646,6 @@ static int sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
/* Failure ignored */
priv->notifier.notifier_call = sh_mobile_lcdc_notify;
fb_register_client(&priv->notifier);
return 0;
err1:
sh_mobile_lcdc_remove(pdev);

View File

@ -87,11 +87,6 @@ struct sh_mobile_lcdc_chan {
unsigned long base_addr_c;
unsigned int line_size;
int (*notify)(struct sh_mobile_lcdc_chan *ch,
enum sh_mobile_lcdc_entity_event event,
const struct fb_videomode *mode,
const struct fb_monspecs *monspec);
/* Backlight */
struct backlight_device *bl;
unsigned int bl_brightness;

View File

@ -62,6 +62,7 @@ void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
void __drm_atomic_helper_connector_reset(struct drm_connector *connector,
struct drm_connector_state *conn_state);
void drm_atomic_helper_connector_reset(struct drm_connector *connector);
void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector);
void
__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
struct drm_connector_state *state);

View File

@ -153,7 +153,7 @@ void drm_client_framebuffer_delete(struct drm_client_buffer *buffer);
int drm_client_modeset_create(struct drm_client_dev *client);
void drm_client_modeset_free(struct drm_client_dev *client);
int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height);
bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation);
bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation);
int drm_client_modeset_commit_force(struct drm_client_dev *client);
int drm_client_modeset_commit(struct drm_client_dev *client);
int drm_client_modeset_dpms(struct drm_client_dev *client, int mode);

View File

@ -463,14 +463,38 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info,
const u32 *formats,
unsigned int num_formats);
/**
* struct drm_connector_tv_margins - TV connector related margins
*
* Describes the margins in pixels to put around the image on TV
* connectors to deal with overscan.
*/
struct drm_connector_tv_margins {
/**
* @bottom: Bottom margin in pixels.
*/
unsigned int bottom;
/**
* @left: Left margin in pixels.
*/
unsigned int left;
/**
* @right: Right margin in pixels.
*/
unsigned int right;
/**
* @top: Top margin in pixels.
*/
unsigned int top;
};
/**
* struct drm_tv_connector_state - TV connector related states
* @subconnector: selected subconnector
* @margins: margins (all margins are expressed in pixels)
* @margins.left: left margin
* @margins.right: right margin
* @margins.top: top margin
* @margins.bottom: bottom margin
* @margins: TV margins
* @mode: TV mode
* @brightness: brightness in percent
* @contrast: contrast in percent
@ -481,12 +505,7 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info,
*/
struct drm_tv_connector_state {
enum drm_mode_subconnector subconnector;
struct {
unsigned int left;
unsigned int right;
unsigned int top;
unsigned int bottom;
} margins;
struct drm_connector_tv_margins margins;
unsigned int mode;
unsigned int brightness;
unsigned int contrast;
@ -923,19 +942,123 @@ struct drm_connector_funcs {
const struct drm_connector_state *state);
};
/* mode specified on the command line */
/**
* struct drm_cmdline_mode - DRM Mode passed through the kernel command-line
*
* Each connector can have an initial mode with additional options
* passed through the kernel command line. This structure allows to
* express those parameters and will be filled by the command-line
* parser.
*/
struct drm_cmdline_mode {
/**
* @name:
*
* Name of the mode.
*/
char name[DRM_DISPLAY_MODE_LEN];
/**
* @specified:
*
* Has a mode been read from the command-line?
*/
bool specified;
/**
* @refresh_specified:
*
* Did the mode have a preferred refresh rate?
*/
bool refresh_specified;
/**
* @bpp_specified:
*
* Did the mode have a preferred BPP?
*/
bool bpp_specified;
int xres, yres;
/**
* @xres:
*
* Active resolution on the X axis, in pixels.
*/
int xres;
/**
* @yres:
*
* Active resolution on the Y axis, in pixels.
*/
int yres;
/**
* @bpp:
*
* Bits per pixels for the mode.
*/
int bpp;
/**
* @refresh:
*
* Refresh rate, in Hertz.
*/
int refresh;
/**
* @rb:
*
* Do we need to use reduced blanking?
*/
bool rb;
/**
* @interlace:
*
* The mode is interlaced.
*/
bool interlace;
/**
* @cvt:
*
* The timings will be calculated using the VESA Coordinated
* Video Timings instead of looking up the mode from a table.
*/
bool cvt;
/**
* @margins:
*
* Add margins to the mode calculation (1.8% of xres rounded
* down to 8 pixels and 1.8% of yres).
*/
bool margins;
/**
* @force:
*
* Ignore the hotplug state of the connector, and force its
* state to one of the DRM_FORCE_* values.
*/
enum drm_connector_force force;
/**
* @rotation_reflection:
*
* Initial rotation and reflection of the mode setup from the
* command line. See DRM_MODE_ROTATE_* and
* DRM_MODE_REFLECT_*. The only rotations supported are
* DRM_MODE_ROTATE_0 and DRM_MODE_ROTATE_180.
*/
unsigned int rotation_reflection;
/**
* @tv_margins: TV margins to apply to the mode.
*/
struct drm_connector_tv_margins tv_margins;
};
/**

View File

@ -1414,6 +1414,13 @@ enum drm_dp_quirk {
* driver still need to implement proper handling for such device.
*/
DP_DPCD_QUIRK_NO_PSR,
/**
* @DP_DPCD_QUIRK_NO_SINK_COUNT:
*
* The device does not set SINK_COUNT to a non-zero value.
* The driver should ignore SINK_COUNT during detection.
*/
DP_DPCD_QUIRK_NO_SINK_COUNT,
};
/**

View File

@ -401,9 +401,4 @@ int drm_gem_dumb_destroy(struct drm_file *file,
struct drm_device *dev,
uint32_t handle);
int drm_gem_pin(struct drm_gem_object *obj);
void drm_gem_unpin(struct drm_gem_object *obj);
void *drm_gem_vmap(struct drm_gem_object *obj);
void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr);
#endif /* __DRM_GEM_H__ */

View File

@ -168,9 +168,6 @@ extern void vc_SAK(struct work_struct *work);
#define CUR_DEFAULT CUR_UNDERLINE
static inline bool con_is_visible(const struct vc_data *vc)
{
return *vc->vc_display_fg == vc;
}
bool con_is_visible(const struct vc_data *vc);
#endif /* _LINUX_CONSOLE_STRUCT_H */

View File

@ -280,10 +280,12 @@ struct dma_buf_ops {
* @file: file pointer used for sharing buffers across, and for refcounting.
* @attachments: list of dma_buf_attachment that denotes all devices attached.
* @ops: dma_buf_ops associated with this buffer object.
* @lock: used internally to serialize list manipulation, attach/detach and vmap/unmap
* @lock: used internally to serialize list manipulation, attach/detach and
* vmap/unmap, and accesses to name
* @vmapping_counter: used internally to refcnt the vmaps
* @vmap_ptr: the current vmap ptr if vmapping_counter > 0
* @exp_name: name of the exporter; useful for debugging.
* @name: userspace-provided name; useful for accounting and debugging.
* @owner: pointer to exporter module; used for refcounting when exporter is a
* kernel module.
* @list_node: node for dma_buf accounting and debugging.
@ -311,6 +313,7 @@ struct dma_buf {
unsigned vmapping_counter;
void *vmap_ptr;
const char *exp_name;
const char *name;
struct module *owner;
struct list_head list_node;
void *priv;

View File

@ -126,39 +126,15 @@ struct fb_cursor_user {
/* The resolution of the passed in fb_info about to change */
#define FB_EVENT_MODE_CHANGE 0x01
/* The display on this fb_info is being suspended, no access to the
* framebuffer is allowed any more after that call returns
*/
#define FB_EVENT_SUSPEND 0x02
/* The display on this fb_info was resumed, you can restore the display
* if you own it
*/
#define FB_EVENT_RESUME 0x03
/* An entry from the modelist was removed */
#define FB_EVENT_MODE_DELETE 0x04
/* A driver registered itself */
#ifdef CONFIG_GUMSTIX_AM200EPD
/* only used by mach-pxa/am200epd.c */
#define FB_EVENT_FB_REGISTERED 0x05
/* A driver unregistered itself */
#define FB_EVENT_FB_UNREGISTERED 0x06
/* CONSOLE-SPECIFIC: get console to framebuffer mapping */
#define FB_EVENT_GET_CONSOLE_MAP 0x07
/* CONSOLE-SPECIFIC: set console to framebuffer mapping */
#define FB_EVENT_SET_CONSOLE_MAP 0x08
/* A hardware display blank change occurred */
#endif
/* A display blank is requested */
#define FB_EVENT_BLANK 0x09
/* Private modelist is to be replaced */
#define FB_EVENT_NEW_MODELIST 0x0A
/* The resolution of the passed in fb_info about to change and
all vc's should be changed */
#define FB_EVENT_MODE_CHANGE_ALL 0x0B
/* A software display blank change occurred */
#define FB_EVENT_CONBLANK 0x0C
/* Get drawing requirements */
#define FB_EVENT_GET_REQ 0x0D
/* Unbind from the console if possible */
#define FB_EVENT_FB_UNBIND 0x0E
/* CONSOLE-SPECIFIC: remap all consoles to new fb - for vga_switcheroo */
#define FB_EVENT_REMAP_ALL_CONSOLE 0x0F
/* A hardware display blank early change occurred */
#define FB_EARLY_EVENT_BLANK 0x10
/* A hardware display blank revert early change occurred */
@ -633,8 +609,8 @@ extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
/* drivers/video/fbmem.c */
extern int register_framebuffer(struct fb_info *fb_info);
extern int unregister_framebuffer(struct fb_info *fb_info);
extern int unlink_framebuffer(struct fb_info *fb_info);
extern void unregister_framebuffer(struct fb_info *fb_info);
extern void unlink_framebuffer(struct fb_info *fb_info);
extern int remove_conflicting_pci_framebuffers(struct pci_dev *pdev, int res_id,
const char *name);
extern int remove_conflicting_framebuffers(struct apertures_struct *a,
@ -660,7 +636,10 @@ extern struct class *fb_class;
for (i = 0; i < FB_MAX; i++) \
if (!registered_fb[i]) {} else
extern int lock_fb_info(struct fb_info *info);
static inline void lock_fb_info(struct fb_info *info)
{
mutex_lock(&info->lock);
}
static inline void unlock_fb_info(struct fb_info *info)
{

View File

@ -4,9 +4,39 @@
#ifdef CONFIG_FRAMEBUFFER_CONSOLE
void __init fb_console_init(void);
void __exit fb_console_exit(void);
int fbcon_fb_registered(struct fb_info *info);
void fbcon_fb_unregistered(struct fb_info *info);
void fbcon_fb_unbind(struct fb_info *info);
void fbcon_suspended(struct fb_info *info);
void fbcon_resumed(struct fb_info *info);
int fbcon_mode_deleted(struct fb_info *info,
struct fb_videomode *mode);
void fbcon_new_modelist(struct fb_info *info);
void fbcon_get_requirement(struct fb_info *info,
struct fb_blit_caps *caps);
void fbcon_fb_blanked(struct fb_info *info, int blank);
void fbcon_update_vcs(struct fb_info *info, bool all);
void fbcon_remap_all(struct fb_info *info);
int fbcon_set_con2fb_map_ioctl(void __user *argp);
int fbcon_get_con2fb_map_ioctl(void __user *argp);
#else
static inline void fb_console_init(void) {}
static inline void fb_console_exit(void) {}
static inline int fbcon_fb_registered(struct fb_info *info) { return 0; }
static inline void fbcon_fb_unregistered(struct fb_info *info) {}
static inline void fbcon_fb_unbind(struct fb_info *info) {}
static inline void fbcon_suspended(struct fb_info *info) {}
static inline void fbcon_resumed(struct fb_info *info) {}
static inline int fbcon_mode_deleted(struct fb_info *info,
struct fb_videomode *mode) { return 0; }
static inline void fbcon_new_modelist(struct fb_info *info) {}
static inline void fbcon_get_requirement(struct fb_info *info,
struct fb_blit_caps *caps) {}
static inline void fbcon_fb_blanked(struct fb_info *info, int blank) {}
static inline void fbcon_update_vcs(struct fb_info *info, bool all) {}
static inline void fbcon_remap_all(struct fb_info *info) {}
static inline int fbcon_set_con2fb_map_ioctl(void __user *argp) { return 0; }
static inline int fbcon_get_con2fb_map_ioctl(void __user *argp) { return 0; }
#endif
#endif /* _LINUX_FBCON_H */

View File

@ -18,6 +18,8 @@ extern "C" {
#define DRM_PANFROST_MMAP_BO 0x03
#define DRM_PANFROST_GET_PARAM 0x04
#define DRM_PANFROST_GET_BO_OFFSET 0x05
#define DRM_PANFROST_PERFCNT_ENABLE 0x06
#define DRM_PANFROST_PERFCNT_DUMP 0x07
#define DRM_IOCTL_PANFROST_SUBMIT DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_SUBMIT, struct drm_panfrost_submit)
#define DRM_IOCTL_PANFROST_WAIT_BO DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_WAIT_BO, struct drm_panfrost_wait_bo)
@ -26,6 +28,15 @@ extern "C" {
#define DRM_IOCTL_PANFROST_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_GET_PARAM, struct drm_panfrost_get_param)
#define DRM_IOCTL_PANFROST_GET_BO_OFFSET DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_GET_BO_OFFSET, struct drm_panfrost_get_bo_offset)
/*
* Unstable ioctl(s): only exposed when the unsafe unstable_ioctls module
* param is set to true.
* All these ioctl(s) are subject to deprecation, so please don't rely on
* them for anything but debugging purpose.
*/
#define DRM_IOCTL_PANFROST_PERFCNT_ENABLE DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_ENABLE, struct drm_panfrost_perfcnt_enable)
#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump)
#define PANFROST_JD_REQ_FS (1 << 0)
/**
* struct drm_panfrost_submit - ioctl argument for submitting commands to the 3D
@ -135,6 +146,19 @@ struct drm_panfrost_get_bo_offset {
__u64 offset;
};
struct drm_panfrost_perfcnt_enable {
__u32 enable;
/*
* On bifrost we have 2 sets of counters, this parameter defines the
* one to track.
*/
__u32 counterset;
};
struct drm_panfrost_perfcnt_dump {
__u64 buf_ptr;
};
#if defined(__cplusplus)
}
#endif

View File

@ -35,7 +35,10 @@ struct dma_buf_sync {
#define DMA_BUF_SYNC_VALID_FLAGS_MASK \
(DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END)
#define DMA_BUF_NAME_LEN 32
#define DMA_BUF_BASE 'b'
#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
#define DMA_BUF_SET_NAME _IOW(DMA_BUF_BASE, 1, const char *)
#endif

View File

@ -91,5 +91,6 @@
#define UDF_SUPER_MAGIC 0x15013346
#define BALLOON_KVM_MAGIC 0x13661366
#define ZSMALLOC_MAGIC 0x58295829
#define DMA_BUF_MAGIC 0x444d4142 /* "DMAB" */
#endif /* __LINUX_MAGIC_H__ */