drm-misc-next for 5.5:
UAPI Changes: -syncobj: allow querying the last submitted timeline value (David) -fourcc: explicitly defineDRM_FORMAT_BIG_ENDIAN as unsigned (Adam) -omap: revert the OMAP_BO_* flags that were added -- no userspace (Sean) Cross-subsystem Changes: -MAINTAINERS: add Mihail as komeda co-maintainer (Mihail) Core Changes: -edid: a few cleanups, add AVI infoframe bar info (Ville) -todo: remove i915 device_link item and add difficulty levels (Daniel) -dp_helpers: add a few new helpers to parse dpcd (Thierry) Driver Changes: -gma500: fix a few memory disclosure leaks (Kangjie) -qxl: convert to use the new drm_gem_object_funcs.mmap (Gerd) -various: open code dp_link helpers in preparation for helper removal (Thierry) Cc: Chunming Zhou <david1.zhou@amd.com> Cc: Adam Jackson <ajax@redhat.com> Cc: Sean Paul <seanpaul@chromium.org> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Cc: Kangjie Lu <kjlu@umn.edu> Cc: Mihail Atanassov <mihail.atanassov@arm.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Thierry Reding <treding@nvidia.com> -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEHF6rntfJ3enn8gh8cywAJXLcr3kFAl2xxiYACgkQcywAJXLc r3mJowf/U9ANPuvF+lahI6IVKpE4KD8Pz+73uNhjmxq5TnmcA7eNJ2pb8HO38tU2 lkchhaQEGZR96nVL962DkegEDu8p08RpYYwKv8r+sDV+zw/aviN/ANLSmTVtZ//m wzgUI7zIqF1WxKdFEzNmQVuY0hYd4fWBn6kGvw1jS/6xL2/3KR6hKVigBZwkICSt /ZiCZyxA7HhAlqzasn+PqGkuLVYv6NvFu4Ug6YG4nBOh57IrKmGt1a6cEUjGsHFf 6Ets5wTPu2ydMRvY+v6rUDDRj6JJQph7Lv4hVKtg13FerJ1+OQ7xjhu4gIk1oNl/ 7zIgRWMVZj79ksMXyk6zgFD2rZAF3w== =Fm3U -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2019-10-24-2' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for 5.5: UAPI Changes: -syncobj: allow querying the last submitted timeline value (David) -fourcc: explicitly defineDRM_FORMAT_BIG_ENDIAN as unsigned (Adam) -omap: revert the OMAP_BO_* flags that were added -- no userspace (Sean) Cross-subsystem Changes: -MAINTAINERS: add Mihail as komeda co-maintainer (Mihail) Core Changes: -edid: a few cleanups, add AVI infoframe bar info (Ville) -todo: remove i915 device_link item and add difficulty levels (Daniel) -dp_helpers: add a few new helpers to parse dpcd (Thierry) Driver Changes: -gma500: fix a few memory disclosure leaks (Kangjie) -qxl: convert to use the new drm_gem_object_funcs.mmap (Gerd) -various: open code dp_link helpers in preparation for helper removal (Thierry) Cc: Chunming Zhou <david1.zhou@amd.com> Cc: Adam Jackson <ajax@redhat.com> Cc: Sean Paul <seanpaul@chromium.org> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Cc: Kangjie Lu <kjlu@umn.edu> Cc: Mihail Atanassov <mihail.atanassov@arm.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Thierry Reding <treding@nvidia.com> Signed-off-by: Dave Airlie <airlied@redhat.com> From: Sean Paul <sean@poorly.run> Link: https://patchwork.freedesktop.org/patch/msgid/20191024155535.GA10294@art_vandelay
This commit is contained in:
commit
a24e4b09dc
@ -37,6 +37,8 @@ Optional properties:
|
||||
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt)
|
||||
to be used for the framebuffer; if not present, the framebuffer may
|
||||
be located anywhere in memory.
|
||||
- arm,malidp-arqos-high-level: integer of u32 value describing the ARQoS
|
||||
levels of DP500's QoS signaling.
|
||||
|
||||
|
||||
Example:
|
||||
@ -54,6 +56,7 @@ Example:
|
||||
clocks = <&oscclk2>, <&fpgaosc0>, <&fpgaosc1>, <&fpgaosc1>;
|
||||
clock-names = "pxlclk", "mclk", "aclk", "pclk";
|
||||
arm,malidp-output-port-lines = /bits/ 8 <8 8 8>;
|
||||
arm,malidp-arqos-high-level = <0xd000d000>;
|
||||
port {
|
||||
dp0_output: endpoint {
|
||||
remote-endpoint = <&tda998x_2_input>;
|
||||
|
@ -20,6 +20,10 @@ Required properties:
|
||||
"rockchip,rk3228-vop";
|
||||
"rockchip,rk3328-vop";
|
||||
|
||||
- reg: Must contain one entry corresponding to the base address and length
|
||||
of the register space. Can optionally contain a second entry
|
||||
corresponding to the CRTC gamma LUT address.
|
||||
|
||||
- interrupts: should contain a list of all VOP IP block interrupts in the
|
||||
order: VSYNC, LCD_SYSTEM. The interrupt specifier
|
||||
format depends on the interrupt controller used.
|
||||
@ -48,7 +52,7 @@ Example:
|
||||
SoC specific DT entry:
|
||||
vopb: vopb@ff930000 {
|
||||
compatible = "rockchip,rk3288-vop";
|
||||
reg = <0xff930000 0x19c>;
|
||||
reg = <0x0 0xff930000 0x0 0x19c>, <0x0 0xff931000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>;
|
||||
clock-names = "aclk_vop", "dclk_vop", "hclk_vop";
|
||||
|
@ -77,9 +77,6 @@ Atomic State Reset and Initialization
|
||||
Atomic State Helper Reference
|
||||
-----------------------------
|
||||
|
||||
.. kernel-doc:: include/drm/drm_atomic_state_helper.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_atomic_state_helper.c
|
||||
:export:
|
||||
|
||||
|
@ -7,6 +7,22 @@ TODO list
|
||||
This section contains a list of smaller janitorial tasks in the kernel DRM
|
||||
graphics subsystem useful as newbie projects. Or for slow rainy days.
|
||||
|
||||
Difficulty
|
||||
----------
|
||||
|
||||
To make it easier task are categorized into different levels:
|
||||
|
||||
Starter: Good tasks to get started with the DRM subsystem.
|
||||
|
||||
Intermediate: Tasks which need some experience with working in the DRM
|
||||
subsystem, or some specific GPU/display graphics knowledge. For debugging issue
|
||||
it's good to have the relevant hardware (or a virtual driver set up) available
|
||||
for testing.
|
||||
|
||||
Advanced: Tricky tasks that need fairly good understanding of the DRM subsystem
|
||||
and graphics topics. Generally need the relevant hardware for development and
|
||||
testing.
|
||||
|
||||
Subsystem-wide refactorings
|
||||
===========================
|
||||
|
||||
@ -20,6 +36,8 @@ implementations), and then remove it.
|
||||
|
||||
Contact: Daniel Vetter, respective driver maintainers
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Convert existing KMS drivers to atomic modesetting
|
||||
--------------------------------------------------
|
||||
|
||||
@ -38,6 +56,8 @@ do by directly using the new atomic helper driver callbacks.
|
||||
|
||||
Contact: Daniel Vetter, respective driver maintainers
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Clean up the clipped coordination confusion around planes
|
||||
---------------------------------------------------------
|
||||
|
||||
@ -50,6 +70,8 @@ helpers.
|
||||
|
||||
Contact: Ville Syrjälä, Daniel Vetter, driver maintainers
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Convert early atomic drivers to async commit helpers
|
||||
----------------------------------------------------
|
||||
|
||||
@ -63,6 +85,8 @@ events for atomic commits correctly. But fixing these bugs is good anyway.
|
||||
|
||||
Contact: Daniel Vetter, respective driver maintainers
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Fallout from atomic KMS
|
||||
-----------------------
|
||||
|
||||
@ -91,6 +115,8 @@ interfaces to fix these issues:
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Get rid of dev->struct_mutex from GEM drivers
|
||||
---------------------------------------------
|
||||
|
||||
@ -114,6 +140,8 @@ fine-grained per-buffer object and per-context lockings scheme. Currently only t
|
||||
|
||||
Contact: Daniel Vetter, respective driver maintainers
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Convert instances of dev_info/dev_err/dev_warn to their DRM_DEV_* equivalent
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
@ -129,6 +157,8 @@ are better.
|
||||
|
||||
Contact: Sean Paul, Maintainer of the driver you plan to convert
|
||||
|
||||
Level: Starter
|
||||
|
||||
Convert drivers to use simple modeset suspend/resume
|
||||
----------------------------------------------------
|
||||
|
||||
@ -139,6 +169,8 @@ of the atomic suspend/resume code in older atomic modeset drivers.
|
||||
|
||||
Contact: Maintainer of the driver you plan to convert
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Convert drivers to use drm_fb_helper_fbdev_setup/teardown()
|
||||
-----------------------------------------------------------
|
||||
|
||||
@ -157,6 +189,8 @@ probably use drm_fb_helper_fbdev_teardown().
|
||||
|
||||
Contact: Maintainer of the driver you plan to convert
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Clean up mmap forwarding
|
||||
------------------------
|
||||
|
||||
@ -166,6 +200,8 @@ There's drm_gem_prime_mmap() for this now, but still needs to be rolled out.
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Generic fbdev defio support
|
||||
---------------------------
|
||||
|
||||
@ -196,6 +232,8 @@ Might be good to also have some igt testcases for this.
|
||||
|
||||
Contact: Daniel Vetter, Noralf Tronnes
|
||||
|
||||
Level: Advanced
|
||||
|
||||
idr_init_base()
|
||||
---------------
|
||||
|
||||
@ -206,6 +244,8 @@ efficient.
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Level: Starter
|
||||
|
||||
struct drm_gem_object_funcs
|
||||
---------------------------
|
||||
|
||||
@ -216,6 +256,8 @@ 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.
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Use DRM_MODESET_LOCK_ALL_* helpers instead of boilerplate
|
||||
---------------------------------------------------------
|
||||
|
||||
@ -231,6 +273,8 @@ As a reference, take a look at the conversions already completed in drm core.
|
||||
|
||||
Contact: Sean Paul, respective driver maintainers
|
||||
|
||||
Level: Starter
|
||||
|
||||
Rename CMA helpers to DMA helpers
|
||||
---------------------------------
|
||||
|
||||
@ -241,6 +285,9 @@ no one knows what that means) since underneath they just use dma_alloc_coherent.
|
||||
|
||||
Contact: Laurent Pinchart, Daniel Vetter
|
||||
|
||||
Level: Intermediate (mostly because it is a huge tasks without good partial
|
||||
milestones, not technically itself that challenging)
|
||||
|
||||
Convert direct mode.vrefresh accesses to use drm_mode_vrefresh()
|
||||
----------------------------------------------------------------
|
||||
|
||||
@ -259,6 +306,8 @@ drm_display_mode to avoid future use.
|
||||
|
||||
Contact: Sean Paul
|
||||
|
||||
Level: Starter
|
||||
|
||||
Remove drm_display_mode.hsync
|
||||
-----------------------------
|
||||
|
||||
@ -269,6 +318,8 @@ it to use drm_mode_hsync() instead.
|
||||
|
||||
Contact: Sean Paul
|
||||
|
||||
Level: Starter
|
||||
|
||||
drm_fb_helper tasks
|
||||
-------------------
|
||||
|
||||
@ -284,6 +335,8 @@ drm_fb_helper tasks
|
||||
removed: drm_fb_helper_single_add_all_connectors(),
|
||||
drm_fb_helper_add_one_connector() and drm_fb_helper_remove_one_connector().
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
connector register/unregister fixes
|
||||
-----------------------------------
|
||||
|
||||
@ -296,21 +349,11 @@ connector register/unregister fixes
|
||||
drm_dp_aux_init, and moving the actual registering into a late_register
|
||||
callback as recommended in the kerneldoc.
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Core refactorings
|
||||
=================
|
||||
|
||||
Clean up the DRM header mess
|
||||
----------------------------
|
||||
|
||||
The DRM subsystem originally had only one huge global header, ``drmP.h``. This
|
||||
is now split up, but many source files still include it. The remaining part of
|
||||
the cleanup work here is to replace any ``#include <drm/drmP.h>`` by only the
|
||||
headers needed (and fixing up any missing pre-declarations in the headers).
|
||||
|
||||
In the end no .c file should need to include ``drmP.h`` anymore.
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Make panic handling work
|
||||
------------------------
|
||||
|
||||
@ -350,6 +393,8 @@ This is a really varied tasks with lots of little bits and pieces:
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Clean up the debugfs support
|
||||
----------------------------
|
||||
|
||||
@ -379,6 +424,8 @@ There's a bunch of issues with it:
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
KMS cleanups
|
||||
------------
|
||||
|
||||
@ -394,6 +441,8 @@ Some of these date from the very introduction of KMS in 2008 ...
|
||||
end, for which we could add drm_*_cleanup_kfree(). And then there's the (for
|
||||
historical reasons) misnamed drm_primary_helper_destroy() function.
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Better Testing
|
||||
==============
|
||||
|
||||
@ -402,6 +451,8 @@ Enable trinity for DRM
|
||||
|
||||
And fix up the fallout. Should be really interesting ...
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Make KMS tests in i-g-t generic
|
||||
-------------------------------
|
||||
|
||||
@ -415,6 +466,8 @@ converting things over. For modeset tests we also first need a bit of
|
||||
infrastructure to use dumb buffers for untiled buffers, to be able to run all
|
||||
the non-i915 specific modeset tests.
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Extend virtual test driver (VKMS)
|
||||
---------------------------------
|
||||
|
||||
@ -424,6 +477,8 @@ fit the available time.
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Level: See details
|
||||
|
||||
Backlight Refactoring
|
||||
---------------------
|
||||
|
||||
@ -437,6 +492,8 @@ Plan to fix this:
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Driver Specific
|
||||
===============
|
||||
|
||||
@ -450,13 +507,6 @@ See drivers/gpu/drm/amd/display/TODO for tasks.
|
||||
|
||||
Contact: Harry Wentland, Alex Deucher
|
||||
|
||||
i915
|
||||
----
|
||||
|
||||
- Our early/late pm callbacks could be removed in favour of using
|
||||
device_link_add to model the dependency between i915 and snd_had. See
|
||||
https://dri.freedesktop.org/docs/drm/driver-api/device_link.html
|
||||
|
||||
Bootsplash
|
||||
==========
|
||||
|
||||
@ -472,5 +522,36 @@ for fbdev.
|
||||
|
||||
Contact: Sam Ravnborg
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Outside DRM
|
||||
===========
|
||||
|
||||
Convert fbdev drivers to DRM
|
||||
----------------------------
|
||||
|
||||
There are plenty of fbdev drivers for older hardware. Some hwardware has
|
||||
become obsolete, but some still provides good(-enough) framebuffers. The
|
||||
drivers that are still useful should be converted to DRM and afterwards
|
||||
removed from fbdev.
|
||||
|
||||
Very simple fbdev drivers can best be converted by starting with a new
|
||||
DRM driver. Simple KMS helpers and SHMEM should be able to handle any
|
||||
existing hardware. The new driver's call-back functions are filled from
|
||||
existing fbdev code.
|
||||
|
||||
More complex fbdev drivers can be refactored step-by-step into a DRM
|
||||
driver with the help of the DRM fbconv helpers. [1] These helpers provide
|
||||
the transition layer between the DRM core infrastructure and the fbdev
|
||||
driver interface. Create a new DRM driver on top of the fbconv helpers,
|
||||
copy over the fbdev driver, and hook it up to the DRM code. Examples for
|
||||
several fbdev drivers are available at [1] and a tutorial of this process
|
||||
available at [2]. The result is a primitive DRM driver that can run X11
|
||||
and Weston.
|
||||
|
||||
- [1] https://gitlab.freedesktop.org/tzimmermann/linux/tree/fbconv
|
||||
- [2] https://gitlab.freedesktop.org/tzimmermann/linux/blob/fbconv/drivers/gpu/drm/drm_fbconv_helper.c
|
||||
|
||||
Contact: Thomas Zimmermann <tzimmermann@suse.de>
|
||||
|
||||
Level: Advanced
|
||||
|
@ -1251,6 +1251,7 @@ F: Documentation/devicetree/bindings/display/arm,hdlcd.txt
|
||||
ARM KOMEDA DRM-KMS DRIVER
|
||||
M: James (Qian) Wang <james.qian.wang@arm.com>
|
||||
M: Liviu Dudau <liviu.dudau@arm.com>
|
||||
M: Mihail Atanassov <mihail.atanassov@arm.com>
|
||||
L: Mali DP Maintainers <malidp@foss.arm.com>
|
||||
S: Supported
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
|
@ -263,6 +263,7 @@ config DRM_VKMS
|
||||
tristate "Virtual KMS (EXPERIMENTAL)"
|
||||
depends on DRM
|
||||
select DRM_KMS_HELPER
|
||||
select CRC32
|
||||
default n
|
||||
help
|
||||
Virtual Kernel Mode-Setting (VKMS) is used for testing or for
|
||||
@ -403,7 +404,7 @@ config DRM_R128
|
||||
|
||||
config DRM_I810
|
||||
tristate "Intel I810"
|
||||
# !PREEMPT because of missing ioctl locking
|
||||
# !PREEMPTION because of missing ioctl locking
|
||||
depends on DRM && AGP && AGP_INTEL && (!PREEMPTION || BROKEN)
|
||||
help
|
||||
Choose this option if you have an Intel I810 graphics card. If M is
|
||||
|
@ -1123,7 +1123,10 @@ void amdgpu_bo_fini(struct amdgpu_device *adev)
|
||||
int amdgpu_bo_fbdev_mmap(struct amdgpu_bo *bo,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return ttm_fbdev_mmap(vma, &bo->tbo);
|
||||
if (vma->vm_pgoff != 0)
|
||||
return -EACCES;
|
||||
|
||||
return ttm_bo_mmap_obj(vma, &bo->tbo);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,6 +106,23 @@ static void dump_block_header(struct seq_file *sf, void __iomem *reg)
|
||||
i, hdr.output_ids[i]);
|
||||
}
|
||||
|
||||
/* On D71, we are using the global line size. From D32, every component have
|
||||
* a line size register to indicate the fifo size.
|
||||
*/
|
||||
static u32 __get_blk_line_size(struct d71_dev *d71, u32 __iomem *reg,
|
||||
u32 max_default)
|
||||
{
|
||||
if (!d71->periph_addr)
|
||||
max_default = malidp_read32(reg, BLK_MAX_LINE_SIZE);
|
||||
|
||||
return max_default;
|
||||
}
|
||||
|
||||
static u32 get_blk_line_size(struct d71_dev *d71, u32 __iomem *reg)
|
||||
{
|
||||
return __get_blk_line_size(d71, reg, d71->max_line_size);
|
||||
}
|
||||
|
||||
static u32 to_rot_ctrl(u32 rot)
|
||||
{
|
||||
u32 lr_ctrl = 0;
|
||||
@ -332,7 +349,56 @@ static void d71_layer_dump(struct komeda_component *c, struct seq_file *sf)
|
||||
seq_printf(sf, "%sAD_V_CROP:\t\t0x%X\n", prefix, v[2]);
|
||||
}
|
||||
|
||||
static int d71_layer_validate(struct komeda_component *c,
|
||||
struct komeda_component_state *state)
|
||||
{
|
||||
struct komeda_layer_state *st = to_layer_st(state);
|
||||
struct komeda_layer *layer = to_layer(c);
|
||||
struct drm_plane_state *plane_st;
|
||||
struct drm_framebuffer *fb;
|
||||
u32 fourcc, line_sz, max_line_sz;
|
||||
|
||||
plane_st = drm_atomic_get_new_plane_state(state->obj.state,
|
||||
state->plane);
|
||||
fb = plane_st->fb;
|
||||
fourcc = fb->format->format;
|
||||
|
||||
if (drm_rotation_90_or_270(st->rot))
|
||||
line_sz = st->vsize - st->afbc_crop_t - st->afbc_crop_b;
|
||||
else
|
||||
line_sz = st->hsize - st->afbc_crop_l - st->afbc_crop_r;
|
||||
|
||||
if (fb->modifier) {
|
||||
if ((fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) ==
|
||||
AFBC_FORMAT_MOD_BLOCK_SIZE_32x8)
|
||||
max_line_sz = layer->line_sz;
|
||||
else
|
||||
max_line_sz = layer->line_sz / 2;
|
||||
|
||||
if (line_sz > max_line_sz) {
|
||||
DRM_DEBUG_ATOMIC("afbc request line_sz: %d exceed the max afbc line_sz: %d.\n",
|
||||
line_sz, max_line_sz);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (fourcc == DRM_FORMAT_YUV420_10BIT && line_sz > 2046 && (st->afbc_crop_l % 4)) {
|
||||
DRM_DEBUG_ATOMIC("YUV420_10BIT input_hsize: %d exceed the max size 2046.\n",
|
||||
line_sz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fourcc == DRM_FORMAT_X0L2 && line_sz > 2046 && (st->addr[0] % 16)) {
|
||||
DRM_DEBUG_ATOMIC("X0L2 input_hsize: %d exceed the max size 2046.\n",
|
||||
line_sz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct komeda_component_funcs d71_layer_funcs = {
|
||||
.validate = d71_layer_validate,
|
||||
.update = d71_layer_update,
|
||||
.disable = d71_layer_disable,
|
||||
.dump_register = d71_layer_dump,
|
||||
@ -365,7 +431,28 @@ static int d71_layer_init(struct d71_dev *d71,
|
||||
else
|
||||
layer->layer_type = KOMEDA_FMT_SIMPLE_LAYER;
|
||||
|
||||
set_range(&layer->hsize_in, 4, d71->max_line_size);
|
||||
if (!d71->periph_addr) {
|
||||
/* D32 or newer product */
|
||||
layer->line_sz = malidp_read32(reg, BLK_MAX_LINE_SIZE);
|
||||
layer->yuv_line_sz = L_INFO_YUV_MAX_LINESZ(layer_info);
|
||||
} else if (d71->max_line_size > 2048) {
|
||||
/* D71 4K */
|
||||
layer->line_sz = d71->max_line_size;
|
||||
layer->yuv_line_sz = layer->line_sz / 2;
|
||||
} else {
|
||||
/* D71 2K */
|
||||
if (layer->layer_type == KOMEDA_FMT_RICH_LAYER) {
|
||||
/* rich layer is 4K configuration */
|
||||
layer->line_sz = d71->max_line_size * 2;
|
||||
layer->yuv_line_sz = layer->line_sz / 2;
|
||||
} else {
|
||||
layer->line_sz = d71->max_line_size;
|
||||
layer->yuv_line_sz = 0;
|
||||
}
|
||||
}
|
||||
|
||||
set_range(&layer->hsize_in, 4, layer->line_sz);
|
||||
|
||||
set_range(&layer->vsize_in, 4, d71->max_vsize);
|
||||
|
||||
malidp_write32(reg, LAYER_PALPHA, D71_PALPHA_DEF_MAP);
|
||||
@ -456,9 +543,11 @@ static int d71_wb_layer_init(struct d71_dev *d71,
|
||||
|
||||
wb_layer = to_layer(c);
|
||||
wb_layer->layer_type = KOMEDA_FMT_WB_LAYER;
|
||||
wb_layer->line_sz = get_blk_line_size(d71, reg);
|
||||
wb_layer->yuv_line_sz = wb_layer->line_sz;
|
||||
|
||||
set_range(&wb_layer->hsize_in, D71_MIN_LINE_SIZE, d71->max_line_size);
|
||||
set_range(&wb_layer->vsize_in, D71_MIN_VERTICAL_SIZE, d71->max_vsize);
|
||||
set_range(&wb_layer->hsize_in, 64, wb_layer->line_sz);
|
||||
set_range(&wb_layer->vsize_in, 64, d71->max_vsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -595,8 +684,8 @@ static int d71_compiz_init(struct d71_dev *d71,
|
||||
|
||||
compiz = to_compiz(c);
|
||||
|
||||
set_range(&compiz->hsize, D71_MIN_LINE_SIZE, d71->max_line_size);
|
||||
set_range(&compiz->vsize, D71_MIN_VERTICAL_SIZE, d71->max_vsize);
|
||||
set_range(&compiz->hsize, 64, get_blk_line_size(d71, reg));
|
||||
set_range(&compiz->vsize, 64, d71->max_vsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -703,7 +792,7 @@ static void d71_scaler_update(struct komeda_component *c,
|
||||
|
||||
static void d71_scaler_dump(struct komeda_component *c, struct seq_file *sf)
|
||||
{
|
||||
u32 v[9];
|
||||
u32 v[10];
|
||||
|
||||
dump_block_header(sf, c->reg);
|
||||
|
||||
@ -723,6 +812,18 @@ static void d71_scaler_dump(struct komeda_component *c, struct seq_file *sf)
|
||||
seq_printf(sf, "SC_H_DELTA_PH:\t\t0x%X\n", v[6]);
|
||||
seq_printf(sf, "SC_V_INIT_PH:\t\t0x%X\n", v[7]);
|
||||
seq_printf(sf, "SC_V_DELTA_PH:\t\t0x%X\n", v[8]);
|
||||
|
||||
get_values_from_reg(c->reg, 0x130, 10, v);
|
||||
seq_printf(sf, "SC_ENH_LIMITS:\t\t0x%X\n", v[0]);
|
||||
seq_printf(sf, "SC_ENH_COEFF0:\t\t0x%X\n", v[1]);
|
||||
seq_printf(sf, "SC_ENH_COEFF1:\t\t0x%X\n", v[2]);
|
||||
seq_printf(sf, "SC_ENH_COEFF2:\t\t0x%X\n", v[3]);
|
||||
seq_printf(sf, "SC_ENH_COEFF3:\t\t0x%X\n", v[4]);
|
||||
seq_printf(sf, "SC_ENH_COEFF4:\t\t0x%X\n", v[5]);
|
||||
seq_printf(sf, "SC_ENH_COEFF5:\t\t0x%X\n", v[6]);
|
||||
seq_printf(sf, "SC_ENH_COEFF6:\t\t0x%X\n", v[7]);
|
||||
seq_printf(sf, "SC_ENH_COEFF7:\t\t0x%X\n", v[8]);
|
||||
seq_printf(sf, "SC_ENH_COEFF8:\t\t0x%X\n", v[9]);
|
||||
}
|
||||
|
||||
static const struct komeda_component_funcs d71_scaler_funcs = {
|
||||
@ -753,7 +854,7 @@ static int d71_scaler_init(struct d71_dev *d71,
|
||||
}
|
||||
|
||||
scaler = to_scaler(c);
|
||||
set_range(&scaler->hsize, 4, 2048);
|
||||
set_range(&scaler->hsize, 4, __get_blk_line_size(d71, reg, 2048));
|
||||
set_range(&scaler->vsize, 4, 4096);
|
||||
scaler->max_downscaling = 6;
|
||||
scaler->max_upscaling = 64;
|
||||
@ -862,7 +963,7 @@ static int d71_splitter_init(struct d71_dev *d71,
|
||||
|
||||
splitter = to_splitter(c);
|
||||
|
||||
set_range(&splitter->hsize, 4, d71->max_line_size);
|
||||
set_range(&splitter->hsize, 4, get_blk_line_size(d71, reg));
|
||||
set_range(&splitter->vsize, 4, d71->max_vsize);
|
||||
|
||||
return 0;
|
||||
@ -933,7 +1034,8 @@ static int d71_merger_init(struct d71_dev *d71,
|
||||
|
||||
merger = to_merger(c);
|
||||
|
||||
set_range(&merger->hsize_merged, 4, 4032);
|
||||
set_range(&merger->hsize_merged, 4,
|
||||
__get_blk_line_size(d71, reg, 4032));
|
||||
set_range(&merger->vsize_merged, 4, 4096);
|
||||
|
||||
return 0;
|
||||
@ -944,13 +1046,26 @@ static void d71_improc_update(struct komeda_component *c,
|
||||
{
|
||||
struct komeda_improc_state *st = to_improc_st(state);
|
||||
u32 __iomem *reg = c->reg;
|
||||
u32 index;
|
||||
u32 index, mask = 0, ctrl = 0;
|
||||
|
||||
for_each_changed_input(state, index)
|
||||
malidp_write32(reg, BLK_INPUT_ID0 + index * 4,
|
||||
to_d71_input_id(state, index));
|
||||
|
||||
malidp_write32(reg, BLK_SIZE, HV_SIZE(st->hsize, st->vsize));
|
||||
malidp_write32(reg, IPS_DEPTH, st->color_depth);
|
||||
|
||||
mask |= IPS_CTRL_YUV | IPS_CTRL_CHD422 | IPS_CTRL_CHD420;
|
||||
|
||||
/* config color format */
|
||||
if (st->color_format == DRM_COLOR_FORMAT_YCRCB420)
|
||||
ctrl |= IPS_CTRL_YUV | IPS_CTRL_CHD422 | IPS_CTRL_CHD420;
|
||||
else if (st->color_format == DRM_COLOR_FORMAT_YCRCB422)
|
||||
ctrl |= IPS_CTRL_YUV | IPS_CTRL_CHD422;
|
||||
else if (st->color_format == DRM_COLOR_FORMAT_YCRCB444)
|
||||
ctrl |= IPS_CTRL_YUV;
|
||||
|
||||
malidp_write32_mask(reg, BLK_CONTROL, mask, ctrl);
|
||||
}
|
||||
|
||||
static void d71_improc_dump(struct komeda_component *c, struct seq_file *sf)
|
||||
|
@ -10,6 +10,7 @@
|
||||
/* Common block registers offset */
|
||||
#define BLK_BLOCK_INFO 0x000
|
||||
#define BLK_PIPELINE_INFO 0x004
|
||||
#define BLK_MAX_LINE_SIZE 0x008
|
||||
#define BLK_VALID_INPUT_ID0 0x020
|
||||
#define BLK_OUTPUT_ID0 0x060
|
||||
#define BLK_INPUT_ID0 0x080
|
||||
@ -321,6 +322,7 @@
|
||||
#define L_INFO_RF BIT(0)
|
||||
#define L_INFO_CM BIT(1)
|
||||
#define L_INFO_ABUF_SIZE(x) (((x) >> 4) & 0x7)
|
||||
#define L_INFO_YUV_MAX_LINESZ(x) (((x) >> 16) & 0xFFFF)
|
||||
|
||||
/* Scaler registers */
|
||||
#define SC_COEFFTAB 0x0DC
|
||||
@ -494,13 +496,6 @@ enum d71_blk_type {
|
||||
#define D71_DEFAULT_PREPRETCH_LINE 5
|
||||
#define D71_BUS_WIDTH_16_BYTES 16
|
||||
|
||||
#define D71_MIN_LINE_SIZE 64
|
||||
#define D71_MIN_VERTICAL_SIZE 64
|
||||
#define D71_SC_MIN_LIN_SIZE 4
|
||||
#define D71_SC_MIN_VERTICAL_SIZE 4
|
||||
#define D71_SC_MAX_LIN_SIZE 2048
|
||||
#define D71_SC_MAX_VERTICAL_SIZE 4096
|
||||
|
||||
#define D71_SC_MAX_UPSCALING 64
|
||||
#define D71_SC_MAX_DOWNSCALING 6
|
||||
#define D71_SC_SPLIT_OVERLAP 8
|
||||
|
@ -17,6 +17,33 @@
|
||||
#include "komeda_dev.h"
|
||||
#include "komeda_kms.h"
|
||||
|
||||
void komeda_crtc_get_color_config(struct drm_crtc_state *crtc_st,
|
||||
u32 *color_depths, u32 *color_formats)
|
||||
{
|
||||
struct drm_connector *conn;
|
||||
struct drm_connector_state *conn_st;
|
||||
u32 conn_color_formats = ~0u;
|
||||
int i, min_bpc = 31, conn_bpc = 0;
|
||||
|
||||
for_each_new_connector_in_state(crtc_st->state, conn, conn_st, i) {
|
||||
if (conn_st->crtc != crtc_st->crtc)
|
||||
continue;
|
||||
|
||||
conn_bpc = conn->display_info.bpc ? conn->display_info.bpc : 8;
|
||||
conn_color_formats &= conn->display_info.color_formats;
|
||||
|
||||
if (conn_bpc < min_bpc)
|
||||
min_bpc = conn_bpc;
|
||||
}
|
||||
|
||||
/* connector doesn't config any color_format, use RGB444 as default */
|
||||
if (!conn_color_formats)
|
||||
conn_color_formats = DRM_COLOR_FORMAT_RGB444;
|
||||
|
||||
*color_depths = GENMASK(min_bpc, 0);
|
||||
*color_formats = conn_color_formats;
|
||||
}
|
||||
|
||||
static void komeda_crtc_update_clock_ratio(struct komeda_crtc_state *kcrtc_st)
|
||||
{
|
||||
u64 pxlclk, aclk;
|
||||
@ -296,7 +323,7 @@ komeda_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct komeda_crtc_state *old_st = to_kcrtc_st(old);
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
struct komeda_pipeline *slave = kcrtc->slave;
|
||||
struct completion *disable_done = &crtc->state->commit->flip_done;
|
||||
struct completion *disable_done;
|
||||
bool needs_phase2 = false;
|
||||
|
||||
DRM_DEBUG_ATOMIC("CRTC%d_DISABLE: active_pipes: 0x%x, affected: 0x%x\n",
|
||||
|
@ -166,6 +166,8 @@ static inline bool has_flip_h(u32 rot)
|
||||
return !!(rotation & DRM_MODE_REFLECT_X);
|
||||
}
|
||||
|
||||
void komeda_crtc_get_color_config(struct drm_crtc_state *crtc_st,
|
||||
u32 *color_depths, u32 *color_formats);
|
||||
unsigned long komeda_crtc_get_aclk(struct komeda_crtc_state *kcrtc_st);
|
||||
|
||||
int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
|
||||
|
@ -227,6 +227,8 @@ struct komeda_layer {
|
||||
/* accepted h/v input range before rotation */
|
||||
struct malidp_range hsize_in, vsize_in;
|
||||
u32 layer_type; /* RICH, SIMPLE or WB */
|
||||
u32 line_sz;
|
||||
u32 yuv_line_sz; /* maximum line size for YUV422 and YUV420 */
|
||||
u32 supported_rots;
|
||||
/* komeda supports layer split which splits a whole image to two parts
|
||||
* left and right and handle them by two individual layer processors
|
||||
@ -323,6 +325,7 @@ struct komeda_improc {
|
||||
|
||||
struct komeda_improc_state {
|
||||
struct komeda_component_state base;
|
||||
u8 color_format, color_depth;
|
||||
u16 hsize, vsize;
|
||||
};
|
||||
|
||||
|
@ -285,6 +285,7 @@ komeda_layer_check_cfg(struct komeda_layer *layer,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
u32 src_x, src_y, src_w, src_h;
|
||||
u32 line_sz, max_line_sz;
|
||||
|
||||
if (!komeda_fb_is_layer_supported(kfb, layer->layer_type, dflow->rot))
|
||||
return -EINVAL;
|
||||
@ -314,6 +315,22 @@ komeda_layer_check_cfg(struct komeda_layer *layer,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (drm_rotation_90_or_270(dflow->rot))
|
||||
line_sz = dflow->in_h;
|
||||
else
|
||||
line_sz = dflow->in_w;
|
||||
|
||||
if (kfb->base.format->hsub > 1)
|
||||
max_line_sz = layer->yuv_line_sz;
|
||||
else
|
||||
max_line_sz = layer->line_sz;
|
||||
|
||||
if (line_sz > max_line_sz) {
|
||||
DRM_DEBUG_ATOMIC("Required line_sz: %d exceeds the max size %d\n",
|
||||
line_sz, max_line_sz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -743,6 +760,7 @@ komeda_improc_validate(struct komeda_improc *improc,
|
||||
struct komeda_data_flow_cfg *dflow)
|
||||
{
|
||||
struct drm_crtc *crtc = kcrtc_st->base.crtc;
|
||||
struct drm_crtc_state *crtc_st = &kcrtc_st->base;
|
||||
struct komeda_component_state *c_st;
|
||||
struct komeda_improc_state *st;
|
||||
|
||||
@ -756,6 +774,34 @@ komeda_improc_validate(struct komeda_improc *improc,
|
||||
st->hsize = dflow->in_w;
|
||||
st->vsize = dflow->in_h;
|
||||
|
||||
if (drm_atomic_crtc_needs_modeset(crtc_st)) {
|
||||
u32 output_depths, output_formats;
|
||||
u32 avail_depths, avail_formats;
|
||||
|
||||
komeda_crtc_get_color_config(crtc_st, &output_depths,
|
||||
&output_formats);
|
||||
|
||||
avail_depths = output_depths & improc->supported_color_depths;
|
||||
if (avail_depths == 0) {
|
||||
DRM_DEBUG_ATOMIC("No available color depths, conn depths: 0x%x & display: 0x%x\n",
|
||||
output_depths,
|
||||
improc->supported_color_depths);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
avail_formats = output_formats &
|
||||
improc->supported_color_formats;
|
||||
if (!avail_formats) {
|
||||
DRM_DEBUG_ATOMIC("No available color_formats, conn formats 0x%x & display: 0x%x\n",
|
||||
output_formats,
|
||||
improc->supported_color_formats);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
st->color_depth = __fls(avail_depths);
|
||||
st->color_format = BIT(__ffs(avail_formats));
|
||||
}
|
||||
|
||||
komeda_component_add_input(&st->base, &dflow->input, 0);
|
||||
komeda_component_set_output(&dflow->input, &improc->base, 0);
|
||||
|
||||
|
@ -141,6 +141,7 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
|
||||
struct komeda_dev *mdev = kms->base.dev_private;
|
||||
struct komeda_wb_connector *kwb_conn;
|
||||
struct drm_writeback_connector *wb_conn;
|
||||
struct drm_display_info *info;
|
||||
u32 *formats, n_formats = 0;
|
||||
int err;
|
||||
|
||||
@ -172,6 +173,10 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms,
|
||||
|
||||
drm_connector_helper_add(&wb_conn->base, &komeda_wb_conn_helper_funcs);
|
||||
|
||||
info = &kwb_conn->base.base.display_info;
|
||||
info->bpc = __fls(kcrtc->master->improc->supported_color_depths);
|
||||
info->color_formats = kcrtc->master->improc->supported_color_formats;
|
||||
|
||||
kcrtc->wb_conn = kwb_conn;
|
||||
|
||||
return 0;
|
||||
|
@ -368,7 +368,7 @@ malidp_verify_afbc_framebuffer(struct drm_device *dev, struct drm_file *file,
|
||||
return false;
|
||||
}
|
||||
|
||||
struct drm_framebuffer *
|
||||
static struct drm_framebuffer *
|
||||
malidp_fb_create(struct drm_device *dev, struct drm_file *file,
|
||||
const struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
@ -491,9 +491,9 @@ void malidp_error(struct malidp_drm *malidp,
|
||||
spin_unlock_irqrestore(&malidp->errors_lock, irqflags);
|
||||
}
|
||||
|
||||
void malidp_error_stats_dump(const char *prefix,
|
||||
struct malidp_error_stats error_stats,
|
||||
struct seq_file *m)
|
||||
static void malidp_error_stats_dump(const char *prefix,
|
||||
struct malidp_error_stats error_stats,
|
||||
struct seq_file *m)
|
||||
{
|
||||
seq_printf(m, "[%s] num_errors : %d\n", prefix,
|
||||
error_stats.num_errors);
|
||||
@ -665,7 +665,7 @@ static ssize_t core_id_show(struct device *dev, struct device_attribute *attr,
|
||||
return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id);
|
||||
}
|
||||
|
||||
DEVICE_ATTR_RO(core_id);
|
||||
static DEVICE_ATTR_RO(core_id);
|
||||
|
||||
static int malidp_init_sysfs(struct device *dev)
|
||||
{
|
||||
@ -817,6 +817,12 @@ static int malidp_bind(struct device *dev)
|
||||
|
||||
malidp->core_id = version;
|
||||
|
||||
ret = of_property_read_u32(dev->of_node,
|
||||
"arm,malidp-arqos-value",
|
||||
&hwdev->arqos_value);
|
||||
if (ret)
|
||||
hwdev->arqos_value = 0x0;
|
||||
|
||||
/* set the number of lines used for output of RGB data */
|
||||
ret = of_property_read_u8_array(dev->of_node,
|
||||
"arm,malidp-output-port-lines",
|
||||
|
@ -379,6 +379,15 @@ static void malidp500_modeset(struct malidp_hw_device *hwdev, struct videomode *
|
||||
malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
|
||||
else
|
||||
malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
|
||||
|
||||
/*
|
||||
* Program the RQoS register to avoid high resolutions flicker
|
||||
* issue on the LS1028A.
|
||||
*/
|
||||
if (hwdev->arqos_value) {
|
||||
val = hwdev->arqos_value;
|
||||
malidp_hw_setbits(hwdev, val, MALIDP500_RQOS_QUALITY);
|
||||
}
|
||||
}
|
||||
|
||||
int malidp_format_get_bpp(u32 fmt)
|
||||
|
@ -251,6 +251,9 @@ struct malidp_hw_device {
|
||||
|
||||
/* size of memory used for rotating layers, up to two banks available */
|
||||
u32 rotation_memory[2];
|
||||
|
||||
/* priority level of RQOS register used for driven the ARQOS signal */
|
||||
u32 arqos_value;
|
||||
};
|
||||
|
||||
static inline u32 malidp_hw_read(struct malidp_hw_device *hwdev, u32 reg)
|
||||
|
@ -210,6 +210,16 @@
|
||||
#define MALIDP500_CONFIG_VALID 0x00f00
|
||||
#define MALIDP500_CONFIG_ID 0x00fd4
|
||||
|
||||
/*
|
||||
* The quality of service (QoS) register on the DP500. RQOS register values
|
||||
* are driven by the ARQOS signal, using AXI transacations, dependent on the
|
||||
* FIFO input level.
|
||||
* The RQOS register can also set QoS levels for:
|
||||
* - RED_ARQOS @ A 4-bit signal value for close to underflow conditions
|
||||
* - GREEN_ARQOS @ A 4-bit signal value for normal conditions
|
||||
*/
|
||||
#define MALIDP500_RQOS_QUALITY 0x00500
|
||||
|
||||
/* register offsets and bits specific to DP550/DP650 */
|
||||
#define MALIDP550_ADDR_SPACE_SIZE 0x10000
|
||||
#define MALIDP550_DE_CONTROL 0x00010
|
||||
|
@ -200,10 +200,7 @@ static struct pci_driver ast_pci_driver = {
|
||||
.driver.pm = &ast_pm_ops,
|
||||
};
|
||||
|
||||
static const struct file_operations ast_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_VRAM_MM_FILE_OPERATIONS
|
||||
};
|
||||
DEFINE_DRM_GEM_FOPS(ast_fops);
|
||||
|
||||
static struct drm_driver driver = {
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM,
|
||||
|
@ -601,7 +601,6 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
|
||||
struct drm_framebuffer *fb = state->base.fb;
|
||||
const struct drm_display_mode *mode;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@ -694,9 +693,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
|
||||
* Swap width and size in case of 90 or 270 degrees rotation
|
||||
*/
|
||||
if (drm_rotation_90_or_270(state->base.rotation)) {
|
||||
tmp = state->src_w;
|
||||
state->src_w = state->src_h;
|
||||
state->src_h = tmp;
|
||||
swap(state->src_w, state->src_h);
|
||||
}
|
||||
|
||||
if (!desc->layout.size &&
|
||||
|
@ -58,10 +58,7 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations bochs_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_VRAM_MM_FILE_OPERATIONS
|
||||
};
|
||||
DEFINE_DRM_GEM_FOPS(bochs_fops);
|
||||
|
||||
static struct drm_driver bochs_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
|
||||
|
@ -87,8 +87,7 @@ config DRM_SIL_SII8620
|
||||
depends on OF
|
||||
select DRM_KMS_HELPER
|
||||
imply EXTCON
|
||||
select INPUT
|
||||
select RC_CORE
|
||||
depends on RC_CORE || !RC_CORE
|
||||
help
|
||||
Silicon Image SII8620 HDMI/MHL bridge chip driver.
|
||||
|
||||
|
@ -39,12 +39,20 @@
|
||||
#define AUX_CH_BUFFER_SIZE 16
|
||||
#define AUX_WAIT_TIMEOUT_MS 15
|
||||
|
||||
static const u8 anx78xx_i2c_addresses[] = {
|
||||
[I2C_IDX_TX_P0] = TX_P0,
|
||||
[I2C_IDX_TX_P1] = TX_P1,
|
||||
[I2C_IDX_TX_P2] = TX_P2,
|
||||
[I2C_IDX_RX_P0] = RX_P0,
|
||||
[I2C_IDX_RX_P1] = RX_P1,
|
||||
static const u8 anx7808_i2c_addresses[] = {
|
||||
[I2C_IDX_TX_P0] = 0x78,
|
||||
[I2C_IDX_TX_P1] = 0x7a,
|
||||
[I2C_IDX_TX_P2] = 0x72,
|
||||
[I2C_IDX_RX_P0] = 0x7e,
|
||||
[I2C_IDX_RX_P1] = 0x80,
|
||||
};
|
||||
|
||||
static const u8 anx781x_i2c_addresses[] = {
|
||||
[I2C_IDX_TX_P0] = 0x70,
|
||||
[I2C_IDX_TX_P1] = 0x7a,
|
||||
[I2C_IDX_TX_P2] = 0x72,
|
||||
[I2C_IDX_RX_P0] = 0x7e,
|
||||
[I2C_IDX_RX_P1] = 0x80,
|
||||
};
|
||||
|
||||
struct anx78xx_platform_data {
|
||||
@ -63,7 +71,6 @@ struct anx78xx {
|
||||
struct i2c_client *client;
|
||||
struct edid *edid;
|
||||
struct drm_connector connector;
|
||||
struct drm_dp_link link;
|
||||
struct anx78xx_platform_data pdata;
|
||||
struct mutex lock;
|
||||
|
||||
@ -740,7 +747,7 @@ static int anx78xx_init_pdata(struct anx78xx *anx78xx)
|
||||
|
||||
static int anx78xx_dp_link_training(struct anx78xx *anx78xx)
|
||||
{
|
||||
u8 dp_bw, value;
|
||||
u8 dp_bw, dpcd[2];
|
||||
int err;
|
||||
|
||||
err = regmap_write(anx78xx->map[I2C_IDX_RX_P0], SP_HDMI_MUTE_CTRL_REG,
|
||||
@ -793,18 +800,34 @@ static int anx78xx_dp_link_training(struct anx78xx *anx78xx)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Check link capabilities */
|
||||
err = drm_dp_link_probe(&anx78xx->aux, &anx78xx->link);
|
||||
if (err < 0) {
|
||||
DRM_ERROR("Failed to probe link capabilities: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* Power up the sink (DP_SET_POWER register is only available on DPCD
|
||||
* v1.1 and later).
|
||||
*/
|
||||
if (anx78xx->dpcd[DP_DPCD_REV] >= 0x11) {
|
||||
err = drm_dp_dpcd_readb(&anx78xx->aux, DP_SET_POWER, &dpcd[0]);
|
||||
if (err < 0) {
|
||||
DRM_ERROR("Failed to read DP_SET_POWER register: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Power up the sink */
|
||||
err = drm_dp_link_power_up(&anx78xx->aux, &anx78xx->link);
|
||||
if (err < 0) {
|
||||
DRM_ERROR("Failed to power up DisplayPort link: %d\n", err);
|
||||
return err;
|
||||
dpcd[0] &= ~DP_SET_POWER_MASK;
|
||||
dpcd[0] |= DP_SET_POWER_D0;
|
||||
|
||||
err = drm_dp_dpcd_writeb(&anx78xx->aux, DP_SET_POWER, dpcd[0]);
|
||||
if (err < 0) {
|
||||
DRM_ERROR("Failed to power up DisplayPort link: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to the DP 1.1 specification, a "Sink Device must
|
||||
* exit the power saving state within 1 ms" (Section 2.5.3.1,
|
||||
* Table 5-52, "Sink Control Field" (register 0x600).
|
||||
*/
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
/* Possibly enable downspread on the sink */
|
||||
@ -843,15 +866,22 @@ static int anx78xx_dp_link_training(struct anx78xx *anx78xx)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
value = drm_dp_link_rate_to_bw_code(anx78xx->link.rate);
|
||||
dpcd[0] = drm_dp_max_link_rate(anx78xx->dpcd);
|
||||
dpcd[0] = drm_dp_link_rate_to_bw_code(dpcd[0]);
|
||||
err = regmap_write(anx78xx->map[I2C_IDX_TX_P0],
|
||||
SP_DP_MAIN_LINK_BW_SET_REG, value);
|
||||
SP_DP_MAIN_LINK_BW_SET_REG, dpcd[0]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = drm_dp_link_configure(&anx78xx->aux, &anx78xx->link);
|
||||
dpcd[1] = drm_dp_max_lane_count(anx78xx->dpcd);
|
||||
|
||||
if (drm_dp_enhanced_frame_cap(anx78xx->dpcd))
|
||||
dpcd[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
|
||||
|
||||
err = drm_dp_dpcd_write(&anx78xx->aux, DP_LINK_BW_SET, dpcd,
|
||||
sizeof(dpcd));
|
||||
if (err < 0) {
|
||||
DRM_ERROR("Failed to configure DisplayPort link: %d\n", err);
|
||||
DRM_ERROR("Failed to configure link: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1316,6 +1346,7 @@ static int anx78xx_i2c_probe(struct i2c_client *client,
|
||||
struct anx78xx *anx78xx;
|
||||
struct anx78xx_platform_data *pdata;
|
||||
unsigned int i, idl, idh, version;
|
||||
const u8 *i2c_addresses;
|
||||
bool found = false;
|
||||
int err;
|
||||
|
||||
@ -1355,15 +1386,16 @@ static int anx78xx_i2c_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
/* Map slave addresses of ANX7814 */
|
||||
i2c_addresses = device_get_match_data(&client->dev);
|
||||
for (i = 0; i < I2C_NUM_ADDRESSES; i++) {
|
||||
struct i2c_client *i2c_dummy;
|
||||
|
||||
i2c_dummy = i2c_new_dummy_device(client->adapter,
|
||||
anx78xx_i2c_addresses[i] >> 1);
|
||||
i2c_addresses[i] >> 1);
|
||||
if (IS_ERR(i2c_dummy)) {
|
||||
err = PTR_ERR(i2c_dummy);
|
||||
DRM_ERROR("Failed to reserve I2C bus %02x: %d\n",
|
||||
anx78xx_i2c_addresses[i], err);
|
||||
i2c_addresses[i], err);
|
||||
goto err_unregister_i2c;
|
||||
}
|
||||
|
||||
@ -1373,7 +1405,7 @@ static int anx78xx_i2c_probe(struct i2c_client *client,
|
||||
if (IS_ERR(anx78xx->map[i])) {
|
||||
err = PTR_ERR(anx78xx->map[i]);
|
||||
DRM_ERROR("Failed regmap initialization %02x\n",
|
||||
anx78xx_i2c_addresses[i]);
|
||||
i2c_addresses[i]);
|
||||
goto err_unregister_i2c;
|
||||
}
|
||||
}
|
||||
@ -1472,10 +1504,10 @@ MODULE_DEVICE_TABLE(i2c, anx78xx_id);
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
static const struct of_device_id anx78xx_match_table[] = {
|
||||
{ .compatible = "analogix,anx7808", },
|
||||
{ .compatible = "analogix,anx7812", },
|
||||
{ .compatible = "analogix,anx7814", },
|
||||
{ .compatible = "analogix,anx7818", },
|
||||
{ .compatible = "analogix,anx7808", .data = anx7808_i2c_addresses },
|
||||
{ .compatible = "analogix,anx7812", .data = anx781x_i2c_addresses },
|
||||
{ .compatible = "analogix,anx7814", .data = anx781x_i2c_addresses },
|
||||
{ .compatible = "analogix,anx7818", .data = anx781x_i2c_addresses },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, anx78xx_match_table);
|
||||
|
@ -6,15 +6,8 @@
|
||||
#ifndef __ANX78xx_H
|
||||
#define __ANX78xx_H
|
||||
|
||||
#define TX_P0 0x70
|
||||
#define TX_P1 0x7a
|
||||
#define TX_P2 0x72
|
||||
|
||||
#define RX_P0 0x7e
|
||||
#define RX_P1 0x80
|
||||
|
||||
/***************************************************************/
|
||||
/* Register definition of device address 0x7e */
|
||||
/* Register definitions for RX_PO */
|
||||
/***************************************************************/
|
||||
|
||||
/*
|
||||
@ -171,7 +164,7 @@
|
||||
#define SP_VSI_RCVD BIT(1)
|
||||
|
||||
/***************************************************************/
|
||||
/* Register definition of device address 0x80 */
|
||||
/* Register definitions for RX_P1 */
|
||||
/***************************************************************/
|
||||
|
||||
/* HDCP BCAPS Shadow Register */
|
||||
@ -217,7 +210,7 @@
|
||||
#define SP_SET_AVMUTE BIT(0)
|
||||
|
||||
/***************************************************************/
|
||||
/* Register definition of device address 0x70 */
|
||||
/* Register definitions for TX_P0 */
|
||||
/***************************************************************/
|
||||
|
||||
/* HDCP Status Register */
|
||||
@ -451,7 +444,7 @@
|
||||
#define SP_DP_BUF_DATA0_REG 0xf0
|
||||
|
||||
/***************************************************************/
|
||||
/* Register definition of device address 0x72 */
|
||||
/* Register definitions for TX_P2 */
|
||||
/***************************************************************/
|
||||
|
||||
/*
|
||||
@ -674,7 +667,7 @@
|
||||
#define SP_INT_CTRL_REG 0xff
|
||||
|
||||
/***************************************************************/
|
||||
/* Register definition of device address 0x7a */
|
||||
/* Register definitions for TX_P1 */
|
||||
/***************************************************************/
|
||||
|
||||
/* DP TX Link Training Control Register */
|
||||
|
@ -842,39 +842,28 @@ static int sii9234_init_resources(struct sii9234 *ctx,
|
||||
|
||||
ctx->client[I2C_MHL] = client;
|
||||
|
||||
ctx->client[I2C_TPI] = i2c_new_dummy(adapter, I2C_TPI_ADDR);
|
||||
if (!ctx->client[I2C_TPI]) {
|
||||
ctx->client[I2C_TPI] = devm_i2c_new_dummy_device(&client->dev, adapter,
|
||||
I2C_TPI_ADDR);
|
||||
if (IS_ERR(ctx->client[I2C_TPI])) {
|
||||
dev_err(ctx->dev, "failed to create TPI client\n");
|
||||
return -ENODEV;
|
||||
return PTR_ERR(ctx->client[I2C_TPI]);
|
||||
}
|
||||
|
||||
ctx->client[I2C_HDMI] = i2c_new_dummy(adapter, I2C_HDMI_ADDR);
|
||||
if (!ctx->client[I2C_HDMI]) {
|
||||
ctx->client[I2C_HDMI] = devm_i2c_new_dummy_device(&client->dev, adapter,
|
||||
I2C_HDMI_ADDR);
|
||||
if (IS_ERR(ctx->client[I2C_HDMI])) {
|
||||
dev_err(ctx->dev, "failed to create HDMI RX client\n");
|
||||
goto fail_tpi;
|
||||
return PTR_ERR(ctx->client[I2C_HDMI]);
|
||||
}
|
||||
|
||||
ctx->client[I2C_CBUS] = i2c_new_dummy(adapter, I2C_CBUS_ADDR);
|
||||
if (!ctx->client[I2C_CBUS]) {
|
||||
ctx->client[I2C_CBUS] = devm_i2c_new_dummy_device(&client->dev, adapter,
|
||||
I2C_CBUS_ADDR);
|
||||
if (IS_ERR(ctx->client[I2C_CBUS])) {
|
||||
dev_err(ctx->dev, "failed to create CBUS client\n");
|
||||
goto fail_hdmi;
|
||||
return PTR_ERR(ctx->client[I2C_CBUS]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_hdmi:
|
||||
i2c_unregister_device(ctx->client[I2C_HDMI]);
|
||||
fail_tpi:
|
||||
i2c_unregister_device(ctx->client[I2C_TPI]);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void sii9234_deinit_resources(struct sii9234 *ctx)
|
||||
{
|
||||
i2c_unregister_device(ctx->client[I2C_CBUS]);
|
||||
i2c_unregister_device(ctx->client[I2C_HDMI]);
|
||||
i2c_unregister_device(ctx->client[I2C_TPI]);
|
||||
}
|
||||
|
||||
static inline struct sii9234 *bridge_to_sii9234(struct drm_bridge *bridge)
|
||||
@ -951,7 +940,6 @@ static int sii9234_remove(struct i2c_client *client)
|
||||
|
||||
sii9234_cable_out(ctx);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
sii9234_deinit_resources(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1760,10 +1760,8 @@ static bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode)
|
||||
|
||||
scancode &= MHL_RCP_KEY_ID_MASK;
|
||||
|
||||
if (!ctx->rc_dev) {
|
||||
dev_dbg(ctx->dev, "RCP input device not initialized\n");
|
||||
if (!IS_ENABLED(CONFIG_RC_CORE) || !ctx->rc_dev)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pressed)
|
||||
rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0);
|
||||
@ -2100,6 +2098,9 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
|
||||
struct rc_dev *rc_dev;
|
||||
int ret;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_RC_CORE))
|
||||
return;
|
||||
|
||||
rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
|
||||
if (!rc_dev) {
|
||||
dev_err(ctx->dev, "Failed to allocate RC device\n");
|
||||
@ -2214,6 +2215,9 @@ static void sii8620_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct sii8620 *ctx = bridge_to_sii8620(bridge);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_RC_CORE))
|
||||
return;
|
||||
|
||||
rc_unregister_device(ctx->rc_dev);
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <uapi/linux/videodev2.h>
|
||||
|
||||
#include <drm/bridge/dw_hdmi.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_edid.h>
|
||||
@ -1743,6 +1744,41 @@ static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
|
||||
HDMI_FC_DATAUTO0_VSD_MASK);
|
||||
}
|
||||
|
||||
static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi)
|
||||
{
|
||||
const struct drm_connector_state *conn_state = hdmi->connector.state;
|
||||
struct hdmi_drm_infoframe frame;
|
||||
u8 buffer[30];
|
||||
ssize_t err;
|
||||
int i;
|
||||
|
||||
if (!hdmi->plat_data->use_drm_infoframe)
|
||||
return;
|
||||
|
||||
hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_DISABLE,
|
||||
HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
|
||||
|
||||
err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state);
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer));
|
||||
if (err < 0) {
|
||||
dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
hdmi_writeb(hdmi, frame.version, HDMI_FC_DRM_HB0);
|
||||
hdmi_writeb(hdmi, frame.length, HDMI_FC_DRM_HB1);
|
||||
|
||||
for (i = 0; i < frame.length; i++)
|
||||
hdmi_writeb(hdmi, buffer[4 + i], HDMI_FC_DRM_PB0 + i);
|
||||
|
||||
hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP);
|
||||
hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_ENABLE,
|
||||
HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
|
||||
}
|
||||
|
||||
static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
@ -2054,7 +2090,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
|
||||
|
||||
/* HDMI Initialization Step E - Configure audio */
|
||||
hdmi_clk_regenerator_update_pixel_clock(hdmi);
|
||||
hdmi_enable_audio_clk(hdmi, true);
|
||||
hdmi_enable_audio_clk(hdmi, hdmi->audio_enable);
|
||||
}
|
||||
|
||||
/* not for DVI mode */
|
||||
@ -2064,6 +2100,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
|
||||
/* HDMI Initialization Step F - Configure AVI InfoFrame */
|
||||
hdmi_config_AVI(hdmi, mode);
|
||||
hdmi_config_vendor_specific_infoframe(hdmi, mode);
|
||||
hdmi_config_drm_infoframe(hdmi);
|
||||
} else {
|
||||
dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
|
||||
}
|
||||
@ -2230,6 +2267,45 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool hdr_metadata_equal(const struct drm_connector_state *old_state,
|
||||
const struct drm_connector_state *new_state)
|
||||
{
|
||||
struct drm_property_blob *old_blob = old_state->hdr_output_metadata;
|
||||
struct drm_property_blob *new_blob = new_state->hdr_output_metadata;
|
||||
|
||||
if (!old_blob || !new_blob)
|
||||
return old_blob == new_blob;
|
||||
|
||||
if (old_blob->length != new_blob->length)
|
||||
return false;
|
||||
|
||||
return !memcmp(old_blob->data, new_blob->data, old_blob->length);
|
||||
}
|
||||
|
||||
static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector_state *old_state =
|
||||
drm_atomic_get_old_connector_state(state, connector);
|
||||
struct drm_connector_state *new_state =
|
||||
drm_atomic_get_new_connector_state(state, connector);
|
||||
struct drm_crtc *crtc = new_state->crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
|
||||
if (!crtc)
|
||||
return 0;
|
||||
|
||||
if (!hdr_metadata_equal(old_state, new_state)) {
|
||||
crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
||||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
crtc_state->mode_changed = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_hdmi_connector_force(struct drm_connector *connector)
|
||||
{
|
||||
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
|
||||
@ -2254,6 +2330,7 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
|
||||
|
||||
static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
|
||||
.get_modes = dw_hdmi_connector_get_modes,
|
||||
.atomic_check = dw_hdmi_connector_atomic_check,
|
||||
};
|
||||
|
||||
static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
|
||||
@ -2274,6 +2351,10 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
|
||||
DRM_MODE_CONNECTOR_HDMIA,
|
||||
hdmi->ddc);
|
||||
|
||||
if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
|
||||
drm_object_attach_property(&connector->base,
|
||||
connector->dev->mode_config.hdr_output_metadata_property, 0);
|
||||
|
||||
drm_connector_attach_encoder(connector, encoder);
|
||||
|
||||
cec_fill_conn_info_from_drm(&conn_info, connector);
|
||||
|
@ -254,6 +254,7 @@
|
||||
#define HDMI_FC_POL2 0x10DB
|
||||
#define HDMI_FC_PRCONF 0x10E0
|
||||
#define HDMI_FC_SCRAMBLER_CTRL 0x10E1
|
||||
#define HDMI_FC_PACKET_TX_EN 0x10E3
|
||||
|
||||
#define HDMI_FC_GMD_STAT 0x1100
|
||||
#define HDMI_FC_GMD_EN 0x1101
|
||||
@ -289,6 +290,37 @@
|
||||
#define HDMI_FC_GMD_PB26 0x111F
|
||||
#define HDMI_FC_GMD_PB27 0x1120
|
||||
|
||||
#define HDMI_FC_DRM_UP 0x1167
|
||||
#define HDMI_FC_DRM_HB0 0x1168
|
||||
#define HDMI_FC_DRM_HB1 0x1169
|
||||
#define HDMI_FC_DRM_PB0 0x116A
|
||||
#define HDMI_FC_DRM_PB1 0x116B
|
||||
#define HDMI_FC_DRM_PB2 0x116C
|
||||
#define HDMI_FC_DRM_PB3 0x116D
|
||||
#define HDMI_FC_DRM_PB4 0x116E
|
||||
#define HDMI_FC_DRM_PB5 0x116F
|
||||
#define HDMI_FC_DRM_PB6 0x1170
|
||||
#define HDMI_FC_DRM_PB7 0x1171
|
||||
#define HDMI_FC_DRM_PB8 0x1172
|
||||
#define HDMI_FC_DRM_PB9 0x1173
|
||||
#define HDMI_FC_DRM_PB10 0x1174
|
||||
#define HDMI_FC_DRM_PB11 0x1175
|
||||
#define HDMI_FC_DRM_PB12 0x1176
|
||||
#define HDMI_FC_DRM_PB13 0x1177
|
||||
#define HDMI_FC_DRM_PB14 0x1178
|
||||
#define HDMI_FC_DRM_PB15 0x1179
|
||||
#define HDMI_FC_DRM_PB16 0x117A
|
||||
#define HDMI_FC_DRM_PB17 0x117B
|
||||
#define HDMI_FC_DRM_PB18 0x117C
|
||||
#define HDMI_FC_DRM_PB19 0x117D
|
||||
#define HDMI_FC_DRM_PB20 0x117E
|
||||
#define HDMI_FC_DRM_PB21 0x117F
|
||||
#define HDMI_FC_DRM_PB22 0x1180
|
||||
#define HDMI_FC_DRM_PB23 0x1181
|
||||
#define HDMI_FC_DRM_PB24 0x1182
|
||||
#define HDMI_FC_DRM_PB25 0x1183
|
||||
#define HDMI_FC_DRM_PB26 0x1184
|
||||
|
||||
#define HDMI_FC_DBGFORCE 0x1200
|
||||
#define HDMI_FC_DBGAUD0CH0 0x1201
|
||||
#define HDMI_FC_DBGAUD1CH0 0x1202
|
||||
@ -744,6 +776,11 @@ enum {
|
||||
HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F,
|
||||
HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0,
|
||||
|
||||
/* FC_PACKET_TX_EN field values */
|
||||
HDMI_FC_PACKET_TX_EN_DRM_MASK = 0x80,
|
||||
HDMI_FC_PACKET_TX_EN_DRM_ENABLE = 0x80,
|
||||
HDMI_FC_PACKET_TX_EN_DRM_DISABLE = 0x00,
|
||||
|
||||
/* FC_AVICONF0-FC_AVICONF3 field values */
|
||||
HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03,
|
||||
HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00,
|
||||
|
@ -229,7 +229,9 @@ static bool tc_test_pattern;
|
||||
module_param_named(test, tc_test_pattern, bool, 0644);
|
||||
|
||||
struct tc_edp_link {
|
||||
struct drm_dp_link base;
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
unsigned int rate;
|
||||
u8 num_lanes;
|
||||
u8 assr;
|
||||
bool scrambler_dis;
|
||||
bool spread;
|
||||
@ -438,9 +440,9 @@ static u32 tc_srcctrl(struct tc_data *tc)
|
||||
reg |= DP0_SRCCTRL_SCRMBLDIS; /* Scrambler Disabled */
|
||||
if (tc->link.spread)
|
||||
reg |= DP0_SRCCTRL_SSCG; /* Spread Spectrum Enable */
|
||||
if (tc->link.base.num_lanes == 2)
|
||||
if (tc->link.num_lanes == 2)
|
||||
reg |= DP0_SRCCTRL_LANES_2; /* Two Main Channel Lanes */
|
||||
if (tc->link.base.rate != 162000)
|
||||
if (tc->link.rate != 162000)
|
||||
reg |= DP0_SRCCTRL_BW27; /* 2.7 Gbps link */
|
||||
return reg;
|
||||
}
|
||||
@ -663,23 +665,35 @@ err:
|
||||
|
||||
static int tc_get_display_props(struct tc_data *tc)
|
||||
{
|
||||
u8 revision, num_lanes;
|
||||
unsigned int rate;
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
/* Read DP Rx Link Capability */
|
||||
ret = drm_dp_link_probe(&tc->aux, &tc->link.base);
|
||||
ret = drm_dp_dpcd_read(&tc->aux, DP_DPCD_REV, tc->link.dpcd,
|
||||
DP_RECEIVER_CAP_SIZE);
|
||||
if (ret < 0)
|
||||
goto err_dpcd_read;
|
||||
if (tc->link.base.rate != 162000 && tc->link.base.rate != 270000) {
|
||||
|
||||
revision = tc->link.dpcd[DP_DPCD_REV];
|
||||
rate = drm_dp_max_link_rate(tc->link.dpcd);
|
||||
num_lanes = drm_dp_max_lane_count(tc->link.dpcd);
|
||||
|
||||
if (rate != 162000 && rate != 270000) {
|
||||
dev_dbg(tc->dev, "Falling to 2.7 Gbps rate\n");
|
||||
tc->link.base.rate = 270000;
|
||||
rate = 270000;
|
||||
}
|
||||
|
||||
if (tc->link.base.num_lanes > 2) {
|
||||
tc->link.rate = rate;
|
||||
|
||||
if (num_lanes > 2) {
|
||||
dev_dbg(tc->dev, "Falling to 2 lanes\n");
|
||||
tc->link.base.num_lanes = 2;
|
||||
num_lanes = 2;
|
||||
}
|
||||
|
||||
tc->link.num_lanes = num_lanes;
|
||||
|
||||
ret = drm_dp_dpcd_readb(&tc->aux, DP_MAX_DOWNSPREAD, ®);
|
||||
if (ret < 0)
|
||||
goto err_dpcd_read;
|
||||
@ -697,11 +711,11 @@ static int tc_get_display_props(struct tc_data *tc)
|
||||
tc->link.assr = reg & DP_ALTERNATE_SCRAMBLER_RESET_ENABLE;
|
||||
|
||||
dev_dbg(tc->dev, "DPCD rev: %d.%d, rate: %s, lanes: %d, framing: %s\n",
|
||||
tc->link.base.revision >> 4, tc->link.base.revision & 0x0f,
|
||||
(tc->link.base.rate == 162000) ? "1.62Gbps" : "2.7Gbps",
|
||||
tc->link.base.num_lanes,
|
||||
(tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) ?
|
||||
"enhanced" : "non-enhanced");
|
||||
revision >> 4, revision & 0x0f,
|
||||
(tc->link.rate == 162000) ? "1.62Gbps" : "2.7Gbps",
|
||||
tc->link.num_lanes,
|
||||
drm_dp_enhanced_frame_cap(tc->link.dpcd) ?
|
||||
"enhanced" : "default");
|
||||
dev_dbg(tc->dev, "Downspread: %s, scrambler: %s\n",
|
||||
tc->link.spread ? "0.5%" : "0.0%",
|
||||
tc->link.scrambler_dis ? "disabled" : "enabled");
|
||||
@ -740,7 +754,7 @@ static int tc_set_video_mode(struct tc_data *tc,
|
||||
*/
|
||||
|
||||
in_bw = mode->clock * bits_per_pixel / 8;
|
||||
out_bw = tc->link.base.num_lanes * tc->link.base.rate;
|
||||
out_bw = tc->link.num_lanes * tc->link.rate;
|
||||
max_tu_symbol = DIV_ROUND_UP(in_bw * TU_SIZE_RECOMMENDED, out_bw);
|
||||
|
||||
dev_dbg(tc->dev, "set mode %dx%d\n",
|
||||
@ -902,7 +916,7 @@ static int tc_main_link_enable(struct tc_data *tc)
|
||||
/* SSCG and BW27 on DP1 must be set to the same as on DP0 */
|
||||
ret = regmap_write(tc->regmap, DP1_SRCCTRL,
|
||||
(tc->link.spread ? DP0_SRCCTRL_SSCG : 0) |
|
||||
((tc->link.base.rate != 162000) ? DP0_SRCCTRL_BW27 : 0));
|
||||
((tc->link.rate != 162000) ? DP0_SRCCTRL_BW27 : 0));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -912,7 +926,7 @@ static int tc_main_link_enable(struct tc_data *tc)
|
||||
|
||||
/* Setup Main Link */
|
||||
dp_phy_ctrl = BGREN | PWR_SW_EN | PHY_A0_EN | PHY_M0_EN;
|
||||
if (tc->link.base.num_lanes == 2)
|
||||
if (tc->link.num_lanes == 2)
|
||||
dp_phy_ctrl |= PHY_2LANE;
|
||||
|
||||
ret = regmap_write(tc->regmap, DP_PHY_CTRL, dp_phy_ctrl);
|
||||
@ -975,7 +989,13 @@ static int tc_main_link_enable(struct tc_data *tc)
|
||||
}
|
||||
|
||||
/* Setup Link & DPRx Config for Training */
|
||||
ret = drm_dp_link_configure(aux, &tc->link.base);
|
||||
tmp[0] = drm_dp_link_rate_to_bw_code(tc->link.rate);
|
||||
tmp[1] = tc->link.num_lanes;
|
||||
|
||||
if (drm_dp_enhanced_frame_cap(tc->link.dpcd))
|
||||
tmp[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
|
||||
|
||||
ret = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, tmp, 2);
|
||||
if (ret < 0)
|
||||
goto err_dpcd_write;
|
||||
|
||||
@ -1019,9 +1039,8 @@ static int tc_main_link_enable(struct tc_data *tc)
|
||||
|
||||
/* Enable DP0 to start Link Training */
|
||||
ret = regmap_write(tc->regmap, DP0CTL,
|
||||
((tc->link.base.capabilities &
|
||||
DP_LINK_CAP_ENHANCED_FRAMING) ? EF_EN : 0) |
|
||||
DP_EN);
|
||||
(drm_dp_enhanced_frame_cap(tc->link.dpcd) ?
|
||||
EF_EN : 0) | DP_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1100,7 +1119,7 @@ static int tc_main_link_enable(struct tc_data *tc)
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
if (tc->link.base.num_lanes == 2) {
|
||||
if (tc->link.num_lanes == 2) {
|
||||
value = (tmp[0] >> 4) & DP_CHANNEL_EQ_BITS;
|
||||
|
||||
if (value != DP_CHANNEL_EQ_BITS) {
|
||||
@ -1171,7 +1190,7 @@ static int tc_stream_enable(struct tc_data *tc)
|
||||
return ret;
|
||||
|
||||
value = VID_MN_GEN | DP_EN;
|
||||
if (tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
|
||||
if (drm_dp_enhanced_frame_cap(tc->link.dpcd))
|
||||
value |= EF_EN;
|
||||
ret = regmap_write(tc->regmap, DP0CTL, value);
|
||||
if (ret)
|
||||
@ -1297,7 +1316,7 @@ static enum drm_mode_status tc_mode_valid(struct drm_bridge *bridge,
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
req = mode->clock * bits_per_pixel / 8;
|
||||
avail = tc->link.base.num_lanes * tc->link.base.rate;
|
||||
avail = tc->link.num_lanes * tc->link.rate;
|
||||
|
||||
if (req > avail)
|
||||
return MODE_BAD;
|
||||
|
@ -510,7 +510,7 @@ static void cirrus_mode_config_init(struct cirrus_device *cirrus)
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
DEFINE_DRM_GEM_SHMEM_FOPS(cirrus_fops);
|
||||
DEFINE_DRM_GEM_FOPS(cirrus_fops);
|
||||
|
||||
static struct drm_driver cirrus_driver = {
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
|
||||
|
@ -1,247 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright 2012 Red Hat
|
||||
*
|
||||
* Authors: Matthew Garrett
|
||||
* Dave Airlie
|
||||
*/
|
||||
#ifndef __CIRRUS_DRV_H__
|
||||
#define __CIRRUS_DRV_H__
|
||||
|
||||
#include <video/vga.h>
|
||||
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
|
||||
#include <drm/ttm/ttm_bo_api.h>
|
||||
#include <drm/ttm/ttm_bo_driver.h>
|
||||
#include <drm/ttm/ttm_placement.h>
|
||||
#include <drm/ttm/ttm_memory.h>
|
||||
#include <drm/ttm/ttm_module.h>
|
||||
|
||||
#include <drm/drm_gem.h>
|
||||
|
||||
#define DRIVER_AUTHOR "Matthew Garrett"
|
||||
|
||||
#define DRIVER_NAME "cirrus"
|
||||
#define DRIVER_DESC "qemu Cirrus emulation"
|
||||
#define DRIVER_DATE "20110418"
|
||||
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
#define DRIVER_PATCHLEVEL 0
|
||||
|
||||
#define CIRRUSFB_CONN_LIMIT 1
|
||||
|
||||
#define RREG8(reg) ioread8(((void __iomem *)cdev->rmmio) + (reg))
|
||||
#define WREG8(reg, v) iowrite8(v, ((void __iomem *)cdev->rmmio) + (reg))
|
||||
#define RREG32(reg) ioread32(((void __iomem *)cdev->rmmio) + (reg))
|
||||
#define WREG32(reg, v) iowrite32(v, ((void __iomem *)cdev->rmmio) + (reg))
|
||||
|
||||
#define SEQ_INDEX 4
|
||||
#define SEQ_DATA 5
|
||||
|
||||
#define WREG_SEQ(reg, v) \
|
||||
do { \
|
||||
WREG8(SEQ_INDEX, reg); \
|
||||
WREG8(SEQ_DATA, v); \
|
||||
} while (0) \
|
||||
|
||||
#define CRT_INDEX 0x14
|
||||
#define CRT_DATA 0x15
|
||||
|
||||
#define WREG_CRT(reg, v) \
|
||||
do { \
|
||||
WREG8(CRT_INDEX, reg); \
|
||||
WREG8(CRT_DATA, v); \
|
||||
} while (0) \
|
||||
|
||||
#define GFX_INDEX 0xe
|
||||
#define GFX_DATA 0xf
|
||||
|
||||
#define WREG_GFX(reg, v) \
|
||||
do { \
|
||||
WREG8(GFX_INDEX, reg); \
|
||||
WREG8(GFX_DATA, v); \
|
||||
} while (0) \
|
||||
|
||||
/*
|
||||
* Cirrus has a "hidden" DAC register that can be accessed by writing to
|
||||
* the pixel mask register to reset the state, then reading from the register
|
||||
* four times. The next write will then pass to the DAC
|
||||
*/
|
||||
#define VGA_DAC_MASK 0x6
|
||||
|
||||
#define WREG_HDR(v) \
|
||||
do { \
|
||||
RREG8(VGA_DAC_MASK); \
|
||||
RREG8(VGA_DAC_MASK); \
|
||||
RREG8(VGA_DAC_MASK); \
|
||||
RREG8(VGA_DAC_MASK); \
|
||||
WREG8(VGA_DAC_MASK, v); \
|
||||
} while (0) \
|
||||
|
||||
|
||||
#define CIRRUS_MAX_FB_HEIGHT 4096
|
||||
#define CIRRUS_MAX_FB_WIDTH 4096
|
||||
|
||||
#define CIRRUS_DPMS_CLEARED (-1)
|
||||
|
||||
#define to_cirrus_crtc(x) container_of(x, struct cirrus_crtc, base)
|
||||
#define to_cirrus_encoder(x) container_of(x, struct cirrus_encoder, base)
|
||||
|
||||
struct cirrus_crtc {
|
||||
struct drm_crtc base;
|
||||
int last_dpms;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct cirrus_fbdev;
|
||||
struct cirrus_mode_info {
|
||||
struct cirrus_crtc *crtc;
|
||||
/* pointer to fbdev info structure */
|
||||
struct cirrus_fbdev *gfbdev;
|
||||
};
|
||||
|
||||
struct cirrus_encoder {
|
||||
struct drm_encoder base;
|
||||
int last_dpms;
|
||||
};
|
||||
|
||||
struct cirrus_connector {
|
||||
struct drm_connector base;
|
||||
};
|
||||
|
||||
struct cirrus_mc {
|
||||
resource_size_t vram_size;
|
||||
resource_size_t vram_base;
|
||||
};
|
||||
|
||||
struct cirrus_device {
|
||||
struct drm_device *dev;
|
||||
unsigned long flags;
|
||||
|
||||
resource_size_t rmmio_base;
|
||||
resource_size_t rmmio_size;
|
||||
void __iomem *rmmio;
|
||||
|
||||
struct cirrus_mc mc;
|
||||
struct cirrus_mode_info mode_info;
|
||||
|
||||
int num_crtc;
|
||||
int fb_mtrr;
|
||||
|
||||
struct {
|
||||
struct ttm_bo_device bdev;
|
||||
} ttm;
|
||||
bool mm_inited;
|
||||
};
|
||||
|
||||
|
||||
struct cirrus_fbdev {
|
||||
struct drm_fb_helper helper; /* must be first */
|
||||
struct drm_framebuffer *gfb;
|
||||
void *sysram;
|
||||
int size;
|
||||
int x1, y1, x2, y2; /* dirty rect */
|
||||
spinlock_t dirty_lock;
|
||||
};
|
||||
|
||||
struct cirrus_bo {
|
||||
struct ttm_buffer_object bo;
|
||||
struct ttm_placement placement;
|
||||
struct ttm_bo_kmap_obj kmap;
|
||||
struct drm_gem_object gem;
|
||||
struct ttm_place placements[3];
|
||||
int pin_count;
|
||||
};
|
||||
#define gem_to_cirrus_bo(gobj) container_of((gobj), struct cirrus_bo, gem)
|
||||
|
||||
static inline struct cirrus_bo *
|
||||
cirrus_bo(struct ttm_buffer_object *bo)
|
||||
{
|
||||
return container_of(bo, struct cirrus_bo, bo);
|
||||
}
|
||||
|
||||
|
||||
#define to_cirrus_obj(x) container_of(x, struct cirrus_gem_object, base)
|
||||
|
||||
/* cirrus_main.c */
|
||||
int cirrus_device_init(struct cirrus_device *cdev,
|
||||
struct drm_device *ddev,
|
||||
struct pci_dev *pdev,
|
||||
uint32_t flags);
|
||||
void cirrus_device_fini(struct cirrus_device *cdev);
|
||||
void cirrus_gem_free_object(struct drm_gem_object *obj);
|
||||
int cirrus_dumb_mmap_offset(struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
uint32_t handle,
|
||||
uint64_t *offset);
|
||||
int cirrus_gem_create(struct drm_device *dev,
|
||||
u32 size, bool iskernel,
|
||||
struct drm_gem_object **obj);
|
||||
int cirrus_dumb_create(struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
|
||||
int cirrus_framebuffer_init(struct drm_device *dev,
|
||||
struct drm_framebuffer *gfb,
|
||||
const struct drm_mode_fb_cmd2 *mode_cmd,
|
||||
struct drm_gem_object *obj);
|
||||
|
||||
bool cirrus_check_framebuffer(struct cirrus_device *cdev, int width, int height,
|
||||
int bpp, int pitch);
|
||||
|
||||
/* cirrus_display.c */
|
||||
int cirrus_modeset_init(struct cirrus_device *cdev);
|
||||
void cirrus_modeset_fini(struct cirrus_device *cdev);
|
||||
|
||||
/* cirrus_fbdev.c */
|
||||
int cirrus_fbdev_init(struct cirrus_device *cdev);
|
||||
void cirrus_fbdev_fini(struct cirrus_device *cdev);
|
||||
|
||||
|
||||
|
||||
/* cirrus_irq.c */
|
||||
void cirrus_driver_irq_preinstall(struct drm_device *dev);
|
||||
int cirrus_driver_irq_postinstall(struct drm_device *dev);
|
||||
void cirrus_driver_irq_uninstall(struct drm_device *dev);
|
||||
irqreturn_t cirrus_driver_irq_handler(int irq, void *arg);
|
||||
|
||||
/* cirrus_kms.c */
|
||||
int cirrus_driver_load(struct drm_device *dev, unsigned long flags);
|
||||
void cirrus_driver_unload(struct drm_device *dev);
|
||||
extern struct drm_ioctl_desc cirrus_ioctls[];
|
||||
extern int cirrus_max_ioctl;
|
||||
|
||||
int cirrus_mm_init(struct cirrus_device *cirrus);
|
||||
void cirrus_mm_fini(struct cirrus_device *cirrus);
|
||||
void cirrus_ttm_placement(struct cirrus_bo *bo, int domain);
|
||||
int cirrus_bo_create(struct drm_device *dev, int size, int align,
|
||||
uint32_t flags, struct cirrus_bo **pcirrusbo);
|
||||
int cirrus_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
|
||||
static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ttm_bo_reserve(&bo->bo, true, no_wait, NULL);
|
||||
if (ret) {
|
||||
if (ret != -ERESTARTSYS && ret != -EBUSY)
|
||||
DRM_ERROR("reserve failed %p\n", bo);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void cirrus_bo_unreserve(struct cirrus_bo *bo)
|
||||
{
|
||||
ttm_bo_unreserve(&bo->bo);
|
||||
}
|
||||
|
||||
int cirrus_bo_push_sysram(struct cirrus_bo *bo);
|
||||
int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr);
|
||||
|
||||
extern int cirrus_bpp;
|
||||
|
||||
#endif /* __CIRRUS_DRV_H__ */
|
@ -132,10 +132,10 @@
|
||||
* planes. Without this property the primary plane is always below the cursor
|
||||
* plane, and ordering between all other planes is undefined. The positive
|
||||
* Z axis points towards the user, i.e. planes with lower Z position values
|
||||
* are underneath planes with higher Z position values. Note that the Z
|
||||
* position value can also be immutable, to inform userspace about the
|
||||
* hard-coded stacking of overlay planes, see
|
||||
* drm_plane_create_zpos_immutable_property().
|
||||
* are underneath planes with higher Z position values. Two planes with the
|
||||
* same Z position value have undefined ordering. Note that the Z position
|
||||
* value can also be immutable, to inform userspace about the hard-coded
|
||||
* stacking of planes, see drm_plane_create_zpos_immutable_property().
|
||||
*
|
||||
* pixel blend mode:
|
||||
* Pixel blend mode is set up with drm_plane_create_blend_mode_property().
|
||||
|
@ -8,11 +8,13 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
|
||||
/*
|
||||
* Unfortunately it turns out that we have a chicken-and-egg situation
|
||||
* here. Quite a few active (mini-)DP-to-HDMI or USB-C-to-HDMI adapters
|
||||
|
@ -120,33 +120,49 @@ u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SI
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);
|
||||
|
||||
void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
|
||||
int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
|
||||
DP_TRAINING_AUX_RD_MASK;
|
||||
u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE],
|
||||
unsigned int lane)
|
||||
{
|
||||
unsigned int offset = DP_ADJUST_REQUEST_POST_CURSOR2;
|
||||
u8 value = dp_link_status(link_status, offset);
|
||||
|
||||
return (value >> (lane << 1)) & 0x3;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor);
|
||||
|
||||
void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
|
||||
{
|
||||
unsigned long rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
|
||||
DP_TRAINING_AUX_RD_MASK;
|
||||
|
||||
if (rd_interval > 4)
|
||||
DRM_DEBUG_KMS("AUX interval %d, out of range (max 4)\n",
|
||||
DRM_DEBUG_KMS("AUX interval %lu, out of range (max 4)\n",
|
||||
rd_interval);
|
||||
|
||||
if (rd_interval == 0 || dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
|
||||
udelay(100);
|
||||
rd_interval = 100;
|
||||
else
|
||||
mdelay(rd_interval * 4);
|
||||
rd_interval *= 4 * USEC_PER_MSEC;
|
||||
|
||||
usleep_range(rd_interval, rd_interval * 2);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
|
||||
|
||||
void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
|
||||
int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
|
||||
DP_TRAINING_AUX_RD_MASK;
|
||||
void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
|
||||
{
|
||||
unsigned long rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
|
||||
DP_TRAINING_AUX_RD_MASK;
|
||||
|
||||
if (rd_interval > 4)
|
||||
DRM_DEBUG_KMS("AUX interval %d, out of range (max 4)\n",
|
||||
DRM_DEBUG_KMS("AUX interval %lu, out of range (max 4)\n",
|
||||
rd_interval);
|
||||
|
||||
if (rd_interval == 0)
|
||||
udelay(400);
|
||||
rd_interval = 400;
|
||||
else
|
||||
mdelay(rd_interval * 4);
|
||||
rd_interval *= 4 * USEC_PER_MSEC;
|
||||
|
||||
usleep_range(rd_interval, rd_interval * 2);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
|
||||
|
||||
@ -220,7 +236,6 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
|
||||
}
|
||||
|
||||
ret = aux->transfer(aux, &msg);
|
||||
|
||||
if (ret >= 0) {
|
||||
native_reply = msg.reply & DP_AUX_NATIVE_REPLY_MASK;
|
||||
if (native_reply == DP_AUX_NATIVE_REPLY_ACK) {
|
||||
@ -336,134 +351,6 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
|
||||
|
||||
/**
|
||||
* drm_dp_link_probe() - probe a DisplayPort link for capabilities
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @link: pointer to structure in which to return link capabilities
|
||||
*
|
||||
* The structure filled in by this function can usually be passed directly
|
||||
* into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
|
||||
* configure the link based on the link's capabilities.
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
|
||||
{
|
||||
u8 values[3];
|
||||
int err;
|
||||
|
||||
memset(link, 0, sizeof(*link));
|
||||
|
||||
err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
link->revision = values[0];
|
||||
link->rate = drm_dp_bw_code_to_link_rate(values[1]);
|
||||
link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
|
||||
|
||||
if (values[2] & DP_ENHANCED_FRAME_CAP)
|
||||
link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_link_probe);
|
||||
|
||||
/**
|
||||
* drm_dp_link_power_up() - power up a DisplayPort link
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @link: pointer to a structure containing the link configuration
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
|
||||
{
|
||||
u8 value;
|
||||
int err;
|
||||
|
||||
/* DP_SET_POWER register is only available on DPCD v1.1 and later */
|
||||
if (link->revision < 0x11)
|
||||
return 0;
|
||||
|
||||
err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
value &= ~DP_SET_POWER_MASK;
|
||||
value |= DP_SET_POWER_D0;
|
||||
|
||||
err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* According to the DP 1.1 specification, a "Sink Device must exit the
|
||||
* power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
|
||||
* Control Field" (register 0x600).
|
||||
*/
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_link_power_up);
|
||||
|
||||
/**
|
||||
* drm_dp_link_power_down() - power down a DisplayPort link
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @link: pointer to a structure containing the link configuration
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link)
|
||||
{
|
||||
u8 value;
|
||||
int err;
|
||||
|
||||
/* DP_SET_POWER register is only available on DPCD v1.1 and later */
|
||||
if (link->revision < 0x11)
|
||||
return 0;
|
||||
|
||||
err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
value &= ~DP_SET_POWER_MASK;
|
||||
value |= DP_SET_POWER_D3;
|
||||
|
||||
err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_link_power_down);
|
||||
|
||||
/**
|
||||
* drm_dp_link_configure() - configure a DisplayPort link
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @link: pointer to a structure containing the link configuration
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
|
||||
{
|
||||
u8 values[2];
|
||||
int err;
|
||||
|
||||
values[0] = drm_dp_link_rate_to_bw_code(link->rate);
|
||||
values[1] = link->num_lanes;
|
||||
|
||||
if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
|
||||
values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
|
||||
|
||||
err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_link_configure);
|
||||
|
||||
/**
|
||||
* drm_dp_downstream_max_clock() - extract branch device max
|
||||
* pixel rate for legacy VGA
|
||||
|
@ -3540,7 +3540,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
|
||||
{
|
||||
struct drm_dp_mst_topology_state *topology_state;
|
||||
struct drm_dp_vcpi_allocation *pos, *vcpi = NULL;
|
||||
int prev_slots, req_slots, ret;
|
||||
int prev_slots, req_slots;
|
||||
|
||||
topology_state = drm_atomic_get_mst_topology_state(state, mgr);
|
||||
if (IS_ERR(topology_state))
|
||||
@ -3587,8 +3587,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
|
||||
}
|
||||
vcpi->vcpi = req_slots;
|
||||
|
||||
ret = req_slots;
|
||||
return ret;
|
||||
return req_slots;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
|
||||
|
||||
@ -4184,9 +4183,6 @@ EXPORT_SYMBOL(drm_dp_mst_topology_state_funcs);
|
||||
struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
|
||||
struct drm_dp_mst_topology_mgr *mgr)
|
||||
{
|
||||
struct drm_device *dev = mgr->dev;
|
||||
|
||||
WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
|
||||
return to_dp_mst_topology_state(drm_atomic_get_private_obj_state(state, &mgr->base));
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
|
||||
|
@ -2192,7 +2192,8 @@ static int standard_timing_level(struct edid *edid)
|
||||
return LEVEL_CVT;
|
||||
if (drm_gtf2_hbreak(edid))
|
||||
return LEVEL_GTF2;
|
||||
return LEVEL_GTF;
|
||||
if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
|
||||
return LEVEL_GTF;
|
||||
}
|
||||
return LEVEL_DMT;
|
||||
}
|
||||
@ -3208,18 +3209,10 @@ static bool drm_valid_cea_vic(u8 vic)
|
||||
return vic > 0 && vic < ARRAY_SIZE(edid_cea_modes);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to
|
||||
* the input VIC from the CEA mode list
|
||||
* @video_code: ID given to each of the CEA modes
|
||||
*
|
||||
* Returns picture aspect ratio
|
||||
*/
|
||||
enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code)
|
||||
static enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code)
|
||||
{
|
||||
return edid_cea_modes[video_code].picture_aspect_ratio;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_get_cea_aspect_ratio);
|
||||
|
||||
/*
|
||||
* Calculate the alternate clock for HDMI modes (those from the HDMI vendor
|
||||
@ -5171,6 +5164,49 @@ drm_hdmi_infoframe_set_hdr_metadata(struct hdmi_drm_infoframe *frame,
|
||||
}
|
||||
EXPORT_SYMBOL(drm_hdmi_infoframe_set_hdr_metadata);
|
||||
|
||||
static u8 drm_mode_hdmi_vic(struct drm_connector *connector,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
bool has_hdmi_infoframe = connector ?
|
||||
connector->display_info.has_hdmi_infoframe : false;
|
||||
|
||||
if (!has_hdmi_infoframe)
|
||||
return 0;
|
||||
|
||||
/* No HDMI VIC when signalling 3D video format */
|
||||
if (mode->flags & DRM_MODE_FLAG_3D_MASK)
|
||||
return 0;
|
||||
|
||||
return drm_match_hdmi_mode(mode);
|
||||
}
|
||||
|
||||
static u8 drm_mode_cea_vic(struct drm_connector *connector,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
u8 vic;
|
||||
|
||||
/*
|
||||
* HDMI spec says if a mode is found in HDMI 1.4b 4K modes
|
||||
* we should send its VIC in vendor infoframes, else send the
|
||||
* VIC in AVI infoframes. Lets check if this mode is present in
|
||||
* HDMI 1.4b 4K modes
|
||||
*/
|
||||
if (drm_mode_hdmi_vic(connector, mode))
|
||||
return 0;
|
||||
|
||||
vic = drm_match_cea_mode(mode);
|
||||
|
||||
/*
|
||||
* HDMI 1.4 VIC range: 1 <= VIC <= 64 (CEA-861-D) but
|
||||
* HDMI 2.0 VIC range: 1 <= VIC <= 107 (CEA-861-F). So we
|
||||
* have to make sure we dont break HDMI 1.4 sinks.
|
||||
*/
|
||||
if (!is_hdmi2_sink(connector) && vic > 64)
|
||||
return 0;
|
||||
|
||||
return vic;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with
|
||||
* data from a DRM display mode
|
||||
@ -5198,29 +5234,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
||||
frame->pixel_repeat = 1;
|
||||
|
||||
frame->video_code = drm_match_cea_mode(mode);
|
||||
|
||||
/*
|
||||
* HDMI 1.4 VIC range: 1 <= VIC <= 64 (CEA-861-D) but
|
||||
* HDMI 2.0 VIC range: 1 <= VIC <= 107 (CEA-861-F). So we
|
||||
* have to make sure we dont break HDMI 1.4 sinks.
|
||||
*/
|
||||
if (!is_hdmi2_sink(connector) && frame->video_code > 64)
|
||||
frame->video_code = 0;
|
||||
|
||||
/*
|
||||
* HDMI spec says if a mode is found in HDMI 1.4b 4K modes
|
||||
* we should send its VIC in vendor infoframes, else send the
|
||||
* VIC in AVI infoframes. Lets check if this mode is present in
|
||||
* HDMI 1.4b 4K modes
|
||||
*/
|
||||
if (frame->video_code) {
|
||||
u8 vendor_if_vic = drm_match_hdmi_mode(mode);
|
||||
bool is_s3d = mode->flags & DRM_MODE_FLAG_3D_MASK;
|
||||
|
||||
if (drm_valid_hdmi_vic(vendor_if_vic) && !is_s3d)
|
||||
frame->video_code = 0;
|
||||
}
|
||||
frame->video_code = drm_mode_cea_vic(connector, mode);
|
||||
|
||||
frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
|
||||
|
||||
@ -5385,6 +5399,23 @@ drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame,
|
||||
}
|
||||
EXPORT_SYMBOL(drm_hdmi_avi_infoframe_quant_range);
|
||||
|
||||
/**
|
||||
* drm_hdmi_avi_infoframe_bars() - fill the HDMI AVI infoframe
|
||||
* bar information
|
||||
* @frame: HDMI AVI infoframe
|
||||
* @conn_state: connector state
|
||||
*/
|
||||
void
|
||||
drm_hdmi_avi_infoframe_bars(struct hdmi_avi_infoframe *frame,
|
||||
const struct drm_connector_state *conn_state)
|
||||
{
|
||||
frame->right_bar = conn_state->tv.margins.right;
|
||||
frame->left_bar = conn_state->tv.margins.left;
|
||||
frame->top_bar = conn_state->tv.margins.top;
|
||||
frame->bottom_bar = conn_state->tv.margins.bottom;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_hdmi_avi_infoframe_bars);
|
||||
|
||||
static enum hdmi_3d_structure
|
||||
s3d_structure_from_display_mode(const struct drm_display_mode *mode)
|
||||
{
|
||||
@ -5437,8 +5468,6 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
|
||||
bool has_hdmi_infoframe = connector ?
|
||||
connector->display_info.has_hdmi_infoframe : false;
|
||||
int err;
|
||||
u32 s3d_flags;
|
||||
u8 vic;
|
||||
|
||||
if (!frame || !mode)
|
||||
return -EINVAL;
|
||||
@ -5446,8 +5475,9 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
|
||||
if (!has_hdmi_infoframe)
|
||||
return -EINVAL;
|
||||
|
||||
vic = drm_match_hdmi_mode(mode);
|
||||
s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK;
|
||||
err = hdmi_vendor_infoframe_init(frame);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Even if it's not absolutely necessary to send the infoframe
|
||||
@ -5458,15 +5488,7 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
|
||||
* mode if the source simply stops sending the infoframe when
|
||||
* it wants to switch from 3D to 2D.
|
||||
*/
|
||||
|
||||
if (vic && s3d_flags)
|
||||
return -EINVAL;
|
||||
|
||||
err = hdmi_vendor_infoframe_init(frame);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
frame->vic = vic;
|
||||
frame->vic = drm_mode_hdmi_vic(connector, mode);
|
||||
frame->s3d_struct = s3d_structure_from_display_mode(mode);
|
||||
|
||||
return 0;
|
||||
|
@ -1099,22 +1099,31 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
int ret;
|
||||
|
||||
/* Check for valid size. */
|
||||
if (obj_size < vma->vm_end - vma->vm_start)
|
||||
return -EINVAL;
|
||||
|
||||
if (obj->funcs && obj->funcs->vm_ops)
|
||||
vma->vm_ops = obj->funcs->vm_ops;
|
||||
else if (dev->driver->gem_vm_ops)
|
||||
vma->vm_ops = dev->driver->gem_vm_ops;
|
||||
else
|
||||
return -EINVAL;
|
||||
if (obj->funcs && obj->funcs->mmap) {
|
||||
ret = obj->funcs->mmap(obj, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
WARN_ON(!(vma->vm_flags & VM_DONTEXPAND));
|
||||
} else {
|
||||
if (obj->funcs && obj->funcs->vm_ops)
|
||||
vma->vm_ops = obj->funcs->vm_ops;
|
||||
else if (dev->driver->gem_vm_ops)
|
||||
vma->vm_ops = dev->driver->gem_vm_ops;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
|
||||
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
|
||||
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
|
||||
}
|
||||
|
||||
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
|
||||
vma->vm_private_data = obj;
|
||||
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
|
||||
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
|
||||
|
||||
/* Take a ref for this mapping of the object, so that the fault
|
||||
* handler can dereference the mmap offset's pointer to the object.
|
||||
|
@ -32,7 +32,7 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
|
||||
.get_sg_table = drm_gem_shmem_get_sg_table,
|
||||
.vmap = drm_gem_shmem_vmap,
|
||||
.vunmap = drm_gem_shmem_vunmap,
|
||||
.vm_ops = &drm_gem_shmem_vm_ops,
|
||||
.mmap = drm_gem_shmem_mmap,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -505,39 +505,30 @@ static void drm_gem_shmem_vm_close(struct vm_area_struct *vma)
|
||||
drm_gem_vm_close(vma);
|
||||
}
|
||||
|
||||
const struct vm_operations_struct drm_gem_shmem_vm_ops = {
|
||||
static const struct vm_operations_struct drm_gem_shmem_vm_ops = {
|
||||
.fault = drm_gem_shmem_fault,
|
||||
.open = drm_gem_shmem_vm_open,
|
||||
.close = drm_gem_shmem_vm_close,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_vm_ops);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_mmap - Memory-map a shmem GEM object
|
||||
* @filp: File object
|
||||
* @obj: gem object
|
||||
* @vma: VMA for the area to be mapped
|
||||
*
|
||||
* This function implements an augmented version of the GEM DRM file mmap
|
||||
* operation for shmem objects. Drivers which employ the shmem helpers should
|
||||
* use this function as their &file_operations.mmap handler in the DRM device file's
|
||||
* file_operations structure.
|
||||
*
|
||||
* Instead of directly referencing this function, drivers should use the
|
||||
* DEFINE_DRM_GEM_SHMEM_FOPS() macro.
|
||||
* use this function as their &drm_gem_object_funcs.mmap handler.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_gem_shmem_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
int drm_gem_shmem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_mmap(filp, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
shmem = to_drm_gem_shmem_obj(vma->vm_private_data);
|
||||
shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
ret = drm_gem_shmem_get_pages(shmem);
|
||||
if (ret) {
|
||||
@ -545,9 +536,10 @@ int drm_gem_shmem_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* VM_PFNMAP was set by drm_gem_mmap() */
|
||||
vma->vm_flags &= ~VM_PFNMAP;
|
||||
vma->vm_flags |= VM_MIXEDMAP;
|
||||
vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND;
|
||||
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
|
||||
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
|
||||
vma->vm_ops = &drm_gem_shmem_vm_ops;
|
||||
|
||||
/* Remove the fake offset */
|
||||
vma->vm_pgoff -= drm_vma_node_start(&shmem->base.vma_node);
|
||||
|
@ -52,5 +52,22 @@ void drm_gem_ttm_print_info(struct drm_printer *p, unsigned int indent,
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_ttm_print_info);
|
||||
|
||||
/**
|
||||
* drm_gem_ttm_mmap() - mmap &ttm_buffer_object
|
||||
* @gem: GEM object.
|
||||
* @vma: vm area.
|
||||
*
|
||||
* This function can be used as &drm_gem_object_funcs.mmap
|
||||
* callback.
|
||||
*/
|
||||
int drm_gem_ttm_mmap(struct drm_gem_object *gem,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(gem);
|
||||
|
||||
return ttm_bo_mmap_obj(vma, bo);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_ttm_mmap);
|
||||
|
||||
MODULE_DESCRIPTION("DRM gem ttm helpers");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -552,13 +552,6 @@ static void drm_gem_vram_bo_driver_evict_flags(struct drm_gem_vram_object *gbo,
|
||||
*pl = gbo->placement;
|
||||
}
|
||||
|
||||
static int drm_gem_vram_bo_driver_verify_access(struct drm_gem_vram_object *gbo,
|
||||
struct file *filp)
|
||||
{
|
||||
return drm_vma_node_verify_access(&gbo->bo.base.vma_node,
|
||||
filp->private_data);
|
||||
}
|
||||
|
||||
static void drm_gem_vram_bo_driver_move_notify(struct drm_gem_vram_object *gbo,
|
||||
bool evict,
|
||||
struct ttm_mem_reg *new_mem)
|
||||
@ -737,6 +730,7 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs = {
|
||||
.unpin = drm_gem_vram_object_unpin,
|
||||
.vmap = drm_gem_vram_object_vmap,
|
||||
.vunmap = drm_gem_vram_object_vunmap,
|
||||
.mmap = drm_gem_ttm_mmap,
|
||||
.print_info = drm_gem_ttm_print_info,
|
||||
};
|
||||
|
||||
@ -822,20 +816,6 @@ static void bo_driver_evict_flags(struct ttm_buffer_object *bo,
|
||||
drm_gem_vram_bo_driver_evict_flags(gbo, placement);
|
||||
}
|
||||
|
||||
static int bo_driver_verify_access(struct ttm_buffer_object *bo,
|
||||
struct file *filp)
|
||||
{
|
||||
struct drm_gem_vram_object *gbo;
|
||||
|
||||
/* TTM may pass BOs that are not GEM VRAM BOs. */
|
||||
if (!drm_is_gem_vram(bo))
|
||||
return -EINVAL;
|
||||
|
||||
gbo = drm_gem_vram_of_bo(bo);
|
||||
|
||||
return drm_gem_vram_bo_driver_verify_access(gbo, filp);
|
||||
}
|
||||
|
||||
static void bo_driver_move_notify(struct ttm_buffer_object *bo,
|
||||
bool evict,
|
||||
struct ttm_mem_reg *new_mem)
|
||||
@ -892,7 +872,6 @@ static struct ttm_bo_driver bo_driver = {
|
||||
.init_mem_type = bo_driver_init_mem_type,
|
||||
.eviction_valuable = ttm_bo_eviction_valuable,
|
||||
.evict_flags = bo_driver_evict_flags,
|
||||
.verify_access = bo_driver_verify_access,
|
||||
.move_notify = bo_driver_move_notify,
|
||||
.io_mem_reserve = bo_driver_io_mem_reserve,
|
||||
.io_mem_free = bo_driver_io_mem_free,
|
||||
@ -971,12 +950,6 @@ static void drm_vram_mm_cleanup(struct drm_vram_mm *vmm)
|
||||
ttm_bo_device_release(&vmm->bdev);
|
||||
}
|
||||
|
||||
static int drm_vram_mm_mmap(struct file *filp, struct vm_area_struct *vma,
|
||||
struct drm_vram_mm *vmm)
|
||||
{
|
||||
return ttm_bo_mmap(filp, vma, &vmm->bdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers for integration with struct drm_device
|
||||
*/
|
||||
@ -1032,30 +1005,3 @@ void drm_vram_helper_release_mm(struct drm_device *dev)
|
||||
dev->vram_mm = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_vram_helper_release_mm);
|
||||
|
||||
/*
|
||||
* Helpers for &struct file_operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* drm_vram_mm_file_operations_mmap() - \
|
||||
Implements &struct file_operations.mmap()
|
||||
* @filp: the mapping's file structure
|
||||
* @vma: the mapping's memory area
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, or
|
||||
* a negative error code otherwise.
|
||||
*/
|
||||
int drm_vram_mm_file_operations_mmap(
|
||||
struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_file *file_priv = filp->private_data;
|
||||
struct drm_device *dev = file_priv->minor->dev;
|
||||
|
||||
if (WARN_ONCE(!dev->vram_mm, "VRAM MM not initialized"))
|
||||
return -EINVAL;
|
||||
|
||||
return drm_vram_mm_mmap(filp, vma, dev->vram_mm);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_vram_mm_file_operations_mmap);
|
||||
|
@ -1021,7 +1021,7 @@ static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd,
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
data[i] = (buf[i] << 1) | !!(buf[i + 1] & BIT(7));
|
||||
data[i] = (buf[i] << 1) | (buf[i + 1] >> 7);
|
||||
}
|
||||
|
||||
MIPI_DBI_DEBUG_COMMAND(*cmd, data, len);
|
||||
|
@ -428,8 +428,6 @@ EXPORT_SYMBOL(drm_mode_config_init);
|
||||
* Note that since this /should/ happen single-threaded at driver/device
|
||||
* teardown time, no locking is required. It's the driver's job to ensure that
|
||||
* this guarantee actually holds true.
|
||||
*
|
||||
* FIXME: cleanup any dangling user buffer objects too
|
||||
*/
|
||||
void drm_mode_config_cleanup(struct drm_device *dev)
|
||||
{
|
||||
|
@ -713,6 +713,15 @@ int drm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
|
||||
struct file *fil;
|
||||
int ret;
|
||||
|
||||
if (obj->funcs && obj->funcs->mmap) {
|
||||
ret = obj->funcs->mmap(obj, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
vma->vm_private_data = obj;
|
||||
drm_gem_object_get(obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
fil = kzalloc(sizeof(*fil), GFP_KERNEL);
|
||||
if (!priv || !fil) {
|
||||
|
@ -1280,7 +1280,7 @@ drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
|
||||
if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (args->pad != 0)
|
||||
if (args->flags != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (args->count_handles == 0)
|
||||
@ -1351,7 +1351,7 @@ int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
|
||||
if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (args->pad != 0)
|
||||
if (args->flags & ~DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED)
|
||||
return -EINVAL;
|
||||
|
||||
if (args->count_handles == 0)
|
||||
@ -1372,25 +1372,32 @@ int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
|
||||
fence = drm_syncobj_fence_get(syncobjs[i]);
|
||||
chain = to_dma_fence_chain(fence);
|
||||
if (chain) {
|
||||
struct dma_fence *iter, *last_signaled = NULL;
|
||||
struct dma_fence *iter, *last_signaled =
|
||||
dma_fence_get(fence);
|
||||
|
||||
dma_fence_chain_for_each(iter, fence) {
|
||||
if (iter->context != fence->context) {
|
||||
dma_fence_put(iter);
|
||||
/* It is most likely that timeline has
|
||||
* unorder points. */
|
||||
break;
|
||||
if (args->flags &
|
||||
DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED) {
|
||||
point = fence->seqno;
|
||||
} else {
|
||||
dma_fence_chain_for_each(iter, fence) {
|
||||
if (iter->context != fence->context) {
|
||||
dma_fence_put(iter);
|
||||
/* It is most likely that timeline has
|
||||
* unorder points. */
|
||||
break;
|
||||
}
|
||||
dma_fence_put(last_signaled);
|
||||
last_signaled = dma_fence_get(iter);
|
||||
}
|
||||
dma_fence_put(last_signaled);
|
||||
last_signaled = dma_fence_get(iter);
|
||||
point = dma_fence_is_signaled(last_signaled) ?
|
||||
last_signaled->seqno :
|
||||
to_dma_fence_chain(last_signaled)->prev_seqno;
|
||||
}
|
||||
point = dma_fence_is_signaled(last_signaled) ?
|
||||
last_signaled->seqno :
|
||||
to_dma_fence_chain(last_signaled)->prev_seqno;
|
||||
dma_fence_put(last_signaled);
|
||||
} else {
|
||||
point = 0;
|
||||
}
|
||||
dma_fence_put(fence);
|
||||
ret = copy_to_user(&points[i], &point, sizeof(uint64_t));
|
||||
ret = ret ? -EFAULT : 0;
|
||||
if (ret)
|
||||
|
@ -1610,7 +1610,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
|
||||
unsigned int flags, pipe, high_pipe;
|
||||
|
||||
if (!dev->irq_enabled)
|
||||
return -EINVAL;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
|
||||
return -EINVAL;
|
||||
@ -1876,7 +1876,7 @@ int drm_crtc_get_sequence_ioctl(struct drm_device *dev, void *data,
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!dev->irq_enabled)
|
||||
return -EINVAL;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
crtc = drm_crtc_find(dev, file_priv, get_seq->crtc_id);
|
||||
if (!crtc)
|
||||
@ -1934,7 +1934,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data,
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!dev->irq_enabled)
|
||||
return -EINVAL;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
crtc = drm_crtc_find(dev, file_priv, queue_seq->crtc_id);
|
||||
if (!crtc)
|
||||
|
@ -405,6 +405,8 @@ static bool cdv_intel_find_dp_pll(const struct gma_limit_t *limit,
|
||||
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
|
||||
struct gma_clock_t clock;
|
||||
|
||||
memset(&clock, 0, sizeof(clock));
|
||||
|
||||
switch (refclk) {
|
||||
case 27000:
|
||||
if (target < 200000) {
|
||||
|
@ -129,6 +129,7 @@ static bool mrst_sdvo_find_best_pll(const struct gma_limit_t *limit,
|
||||
s32 freq_error, min_error = 100000;
|
||||
|
||||
memset(best_clock, 0, sizeof(*best_clock));
|
||||
memset(&clock, 0, sizeof(clock));
|
||||
|
||||
for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
|
||||
for (clock.n = limit->n.min; clock.n <= limit->n.max;
|
||||
@ -185,6 +186,7 @@ static bool mrst_lvds_find_best_pll(const struct gma_limit_t *limit,
|
||||
int err = target;
|
||||
|
||||
memset(best_clock, 0, sizeof(*best_clock));
|
||||
memset(&clock, 0, sizeof(clock));
|
||||
|
||||
for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
|
||||
for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max;
|
||||
|
@ -26,10 +26,7 @@
|
||||
#include "hibmc_drm_drv.h"
|
||||
#include "hibmc_drm_regs.h"
|
||||
|
||||
static const struct file_operations hibmc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_VRAM_MM_FILE_OPERATIONS
|
||||
};
|
||||
DEFINE_DRM_GEM_FOPS(hibmc_fops);
|
||||
|
||||
static irqreturn_t hibmc_drm_interrupt(int irq, void *arg)
|
||||
{
|
||||
|
@ -806,8 +806,8 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
|
||||
tda998x_edid_delay_start(priv);
|
||||
} else {
|
||||
schedule_work(&priv->detect_work);
|
||||
cec_notifier_set_phys_addr(priv->cec_notify,
|
||||
CEC_PHYS_ADDR_INVALID);
|
||||
cec_notifier_phys_addr_invalidate(
|
||||
priv->cec_notify);
|
||||
}
|
||||
|
||||
handled = true;
|
||||
@ -1791,8 +1791,7 @@ static void tda998x_destroy(struct device *dev)
|
||||
|
||||
i2c_unregister_device(priv->cec);
|
||||
|
||||
if (priv->cec_notify)
|
||||
cec_notifier_put(priv->cec_notify);
|
||||
cec_notifier_conn_unregister(priv->cec_notify);
|
||||
}
|
||||
|
||||
static int tda998x_create(struct device *dev)
|
||||
@ -1917,7 +1916,7 @@ static int tda998x_create(struct device *dev)
|
||||
cec_write(priv, REG_CEC_RXSHPDINTENA, CEC_RXSHPDLEV_HPD);
|
||||
}
|
||||
|
||||
priv->cec_notify = cec_notifier_get(dev);
|
||||
priv->cec_notify = cec_notifier_conn_register(dev, NULL, NULL);
|
||||
if (!priv->cec_notify) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
|
@ -9,5 +9,6 @@ config DRM_LIMA
|
||||
depends on COMMON_CLK
|
||||
depends on OF
|
||||
select DRM_SCHED
|
||||
select DRM_GEM_SHMEM_HELPER
|
||||
help
|
||||
DRM driver for ARM Mali 400/450 GPUs.
|
||||
|
@ -13,9 +13,7 @@ lima-y := \
|
||||
lima_vm.o \
|
||||
lima_sched.o \
|
||||
lima_ctx.o \
|
||||
lima_gem_prime.o \
|
||||
lima_dlbu.o \
|
||||
lima_bcast.o \
|
||||
lima_object.o
|
||||
lima_bcast.o
|
||||
|
||||
obj-$(CONFIG_DRM_LIMA) += lima.o
|
||||
|
@ -314,7 +314,7 @@ int lima_device_init(struct lima_device *ldev)
|
||||
ldev->va_end = LIMA_VA_RESERVE_START;
|
||||
ldev->dlbu_cpu = dma_alloc_wc(
|
||||
ldev->dev, LIMA_PAGE_SIZE,
|
||||
&ldev->dlbu_dma, GFP_KERNEL);
|
||||
&ldev->dlbu_dma, GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!ldev->dlbu_cpu) {
|
||||
err = -ENOMEM;
|
||||
goto err_out2;
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include "lima_drv.h"
|
||||
#include "lima_gem.h"
|
||||
#include "lima_gem_prime.h"
|
||||
#include "lima_vm.h"
|
||||
|
||||
int lima_sched_timeout_ms;
|
||||
@ -240,16 +239,7 @@ static const struct drm_ioctl_desc lima_drm_driver_ioctls[] = {
|
||||
DRM_IOCTL_DEF_DRV(LIMA_CTX_FREE, lima_ioctl_ctx_free, DRM_RENDER_ALLOW),
|
||||
};
|
||||
|
||||
static const struct file_operations lima_drm_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.mmap = lima_gem_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_FOPS(lima_drm_driver_fops);
|
||||
|
||||
static struct drm_driver lima_drm_driver = {
|
||||
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ,
|
||||
@ -258,10 +248,6 @@ static struct drm_driver lima_drm_driver = {
|
||||
.ioctls = lima_drm_driver_ioctls,
|
||||
.num_ioctls = ARRAY_SIZE(lima_drm_driver_ioctls),
|
||||
.fops = &lima_drm_driver_fops,
|
||||
.gem_free_object_unlocked = lima_gem_free_object,
|
||||
.gem_open_object = lima_gem_object_open,
|
||||
.gem_close_object = lima_gem_object_close,
|
||||
.gem_vm_ops = &lima_gem_vm_ops,
|
||||
.name = "lima",
|
||||
.desc = "lima DRM",
|
||||
.date = "20190217",
|
||||
@ -269,11 +255,11 @@ static struct drm_driver lima_drm_driver = {
|
||||
.minor = 0,
|
||||
.patchlevel = 0,
|
||||
|
||||
.gem_create_object = lima_gem_create_object,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_import_sg_table = lima_gem_prime_import_sg_table,
|
||||
.gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table,
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.gem_prime_get_sg_table = lima_gem_prime_get_sg_table,
|
||||
.gem_prime_mmap = lima_gem_prime_mmap,
|
||||
.gem_prime_mmap = drm_gem_prime_mmap,
|
||||
};
|
||||
|
||||
static int lima_pdev_probe(struct platform_device *pdev)
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sync_file.h>
|
||||
#include <linux/pfn_t.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_syncobj.h>
|
||||
@ -13,40 +13,55 @@
|
||||
|
||||
#include "lima_drv.h"
|
||||
#include "lima_gem.h"
|
||||
#include "lima_gem_prime.h"
|
||||
#include "lima_vm.h"
|
||||
#include "lima_object.h"
|
||||
|
||||
int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
|
||||
u32 size, u32 flags, u32 *handle)
|
||||
{
|
||||
int err;
|
||||
struct lima_bo *bo;
|
||||
struct lima_device *ldev = to_lima_dev(dev);
|
||||
gfp_t mask;
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
struct drm_gem_object *obj;
|
||||
struct sg_table *sgt;
|
||||
|
||||
bo = lima_bo_create(ldev, size, flags, NULL);
|
||||
if (IS_ERR(bo))
|
||||
return PTR_ERR(bo);
|
||||
shmem = drm_gem_shmem_create(dev, size);
|
||||
if (IS_ERR(shmem))
|
||||
return PTR_ERR(shmem);
|
||||
|
||||
err = drm_gem_handle_create(file, &bo->gem, handle);
|
||||
obj = &shmem->base;
|
||||
|
||||
/* Mali Utgard GPU can only support 32bit address space */
|
||||
mask = mapping_gfp_mask(obj->filp->f_mapping);
|
||||
mask &= ~__GFP_HIGHMEM;
|
||||
mask |= __GFP_DMA32;
|
||||
mapping_set_gfp_mask(obj->filp->f_mapping, mask);
|
||||
|
||||
sgt = drm_gem_shmem_get_pages_sgt(obj);
|
||||
if (IS_ERR(sgt)) {
|
||||
err = PTR_ERR(sgt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = drm_gem_handle_create(file, obj, handle);
|
||||
|
||||
out:
|
||||
/* drop reference from allocate - handle holds it now */
|
||||
drm_gem_object_put_unlocked(&bo->gem);
|
||||
drm_gem_object_put_unlocked(obj);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void lima_gem_free_object(struct drm_gem_object *obj)
|
||||
static void lima_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct lima_bo *bo = to_lima_bo(obj);
|
||||
|
||||
if (!list_empty(&bo->va))
|
||||
dev_err(obj->dev->dev, "lima gem free bo still has va\n");
|
||||
|
||||
lima_bo_destroy(bo);
|
||||
drm_gem_shmem_free_object(obj);
|
||||
}
|
||||
|
||||
int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file)
|
||||
static int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file)
|
||||
{
|
||||
struct lima_bo *bo = to_lima_bo(obj);
|
||||
struct lima_drm_priv *priv = to_lima_drm_priv(file);
|
||||
@ -55,7 +70,7 @@ int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file)
|
||||
return lima_vm_bo_add(vm, bo, true);
|
||||
}
|
||||
|
||||
void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file)
|
||||
static void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file)
|
||||
{
|
||||
struct lima_bo *bo = to_lima_bo(obj);
|
||||
struct lima_drm_priv *priv = to_lima_drm_priv(file);
|
||||
@ -64,13 +79,41 @@ void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file)
|
||||
lima_vm_bo_del(vm, bo);
|
||||
}
|
||||
|
||||
static const struct drm_gem_object_funcs lima_gem_funcs = {
|
||||
.free = lima_gem_free_object,
|
||||
.open = lima_gem_object_open,
|
||||
.close = lima_gem_object_close,
|
||||
.print_info = drm_gem_shmem_print_info,
|
||||
.pin = drm_gem_shmem_pin,
|
||||
.unpin = drm_gem_shmem_unpin,
|
||||
.get_sg_table = drm_gem_shmem_get_sg_table,
|
||||
.vmap = drm_gem_shmem_vmap,
|
||||
.vunmap = drm_gem_shmem_vunmap,
|
||||
.mmap = drm_gem_shmem_mmap,
|
||||
};
|
||||
|
||||
struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t size)
|
||||
{
|
||||
struct lima_bo *bo;
|
||||
|
||||
bo = kzalloc(sizeof(*bo), GFP_KERNEL);
|
||||
if (!bo)
|
||||
return NULL;
|
||||
|
||||
mutex_init(&bo->lock);
|
||||
INIT_LIST_HEAD(&bo->va);
|
||||
|
||||
bo->base.base.funcs = &lima_gem_funcs;
|
||||
|
||||
return &bo->base.base;
|
||||
}
|
||||
|
||||
int lima_gem_get_info(struct drm_file *file, u32 handle, u32 *va, u64 *offset)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
struct lima_bo *bo;
|
||||
struct lima_drm_priv *priv = to_lima_drm_priv(file);
|
||||
struct lima_vm *vm = priv->vm;
|
||||
int err;
|
||||
|
||||
obj = drm_gem_object_lookup(file, handle);
|
||||
if (!obj)
|
||||
@ -80,53 +123,9 @@ int lima_gem_get_info(struct drm_file *file, u32 handle, u32 *va, u64 *offset)
|
||||
|
||||
*va = lima_vm_get_va(vm, bo);
|
||||
|
||||
err = drm_gem_create_mmap_offset(obj);
|
||||
if (!err)
|
||||
*offset = drm_vma_node_offset_addr(&obj->vma_node);
|
||||
*offset = drm_vma_node_offset_addr(&obj->vma_node);
|
||||
|
||||
drm_gem_object_put_unlocked(obj);
|
||||
return err;
|
||||
}
|
||||
|
||||
static vm_fault_t lima_gem_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct drm_gem_object *obj = vma->vm_private_data;
|
||||
struct lima_bo *bo = to_lima_bo(obj);
|
||||
pfn_t pfn;
|
||||
pgoff_t pgoff;
|
||||
|
||||
/* We don't use vmf->pgoff since that has the fake offset: */
|
||||
pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
|
||||
pfn = __pfn_to_pfn_t(page_to_pfn(bo->pages[pgoff]), PFN_DEV);
|
||||
|
||||
return vmf_insert_mixed(vma, vmf->address, pfn);
|
||||
}
|
||||
|
||||
const struct vm_operations_struct lima_gem_vm_ops = {
|
||||
.fault = lima_gem_fault,
|
||||
.open = drm_gem_vm_open,
|
||||
.close = drm_gem_vm_close,
|
||||
};
|
||||
|
||||
void lima_set_vma_flags(struct vm_area_struct *vma)
|
||||
{
|
||||
pgprot_t prot = vm_get_page_prot(vma->vm_flags);
|
||||
|
||||
vma->vm_flags |= VM_MIXEDMAP;
|
||||
vma->vm_flags &= ~VM_PFNMAP;
|
||||
vma->vm_page_prot = pgprot_writecombine(prot);
|
||||
}
|
||||
|
||||
int lima_gem_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_mmap(filp, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
lima_set_vma_flags(vma);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -136,7 +135,7 @@ static int lima_gem_sync_bo(struct lima_sched_task *task, struct lima_bo *bo,
|
||||
int err = 0;
|
||||
|
||||
if (!write) {
|
||||
err = dma_resv_reserve_shared(bo->gem.resv, 1);
|
||||
err = dma_resv_reserve_shared(lima_bo_resv(bo), 1);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -145,62 +144,7 @@ static int lima_gem_sync_bo(struct lima_sched_task *task, struct lima_bo *bo,
|
||||
if (explicit)
|
||||
return 0;
|
||||
|
||||
return drm_gem_fence_array_add_implicit(&task->deps, &bo->gem, write);
|
||||
}
|
||||
|
||||
static int lima_gem_lock_bos(struct lima_bo **bos, u32 nr_bos,
|
||||
struct ww_acquire_ctx *ctx)
|
||||
{
|
||||
int i, ret = 0, contended, slow_locked = -1;
|
||||
|
||||
ww_acquire_init(ctx, &reservation_ww_class);
|
||||
|
||||
retry:
|
||||
for (i = 0; i < nr_bos; i++) {
|
||||
if (i == slow_locked) {
|
||||
slow_locked = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = ww_mutex_lock_interruptible(&bos[i]->gem.resv->lock, ctx);
|
||||
if (ret < 0) {
|
||||
contended = i;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ww_acquire_done(ctx);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
for (i--; i >= 0; i--)
|
||||
ww_mutex_unlock(&bos[i]->gem.resv->lock);
|
||||
|
||||
if (slow_locked >= 0)
|
||||
ww_mutex_unlock(&bos[slow_locked]->gem.resv->lock);
|
||||
|
||||
if (ret == -EDEADLK) {
|
||||
/* we lost out in a seqno race, lock and retry.. */
|
||||
ret = ww_mutex_lock_slow_interruptible(
|
||||
&bos[contended]->gem.resv->lock, ctx);
|
||||
if (!ret) {
|
||||
slow_locked = contended;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
ww_acquire_fini(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lima_gem_unlock_bos(struct lima_bo **bos, u32 nr_bos,
|
||||
struct ww_acquire_ctx *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_bos; i++)
|
||||
ww_mutex_unlock(&bos[i]->gem.resv->lock);
|
||||
ww_acquire_fini(ctx);
|
||||
return drm_gem_fence_array_add_implicit(&task->deps, &bo->base.base, write);
|
||||
}
|
||||
|
||||
static int lima_gem_add_deps(struct drm_file *file, struct lima_submit *submit)
|
||||
@ -268,7 +212,8 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit)
|
||||
bos[i] = bo;
|
||||
}
|
||||
|
||||
err = lima_gem_lock_bos(bos, submit->nr_bos, &ctx);
|
||||
err = drm_gem_lock_reservations((struct drm_gem_object **)bos,
|
||||
submit->nr_bos, &ctx);
|
||||
if (err)
|
||||
goto err_out0;
|
||||
|
||||
@ -296,15 +241,16 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit)
|
||||
|
||||
for (i = 0; i < submit->nr_bos; i++) {
|
||||
if (submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE)
|
||||
dma_resv_add_excl_fence(bos[i]->gem.resv, fence);
|
||||
dma_resv_add_excl_fence(lima_bo_resv(bos[i]), fence);
|
||||
else
|
||||
dma_resv_add_shared_fence(bos[i]->gem.resv, fence);
|
||||
dma_resv_add_shared_fence(lima_bo_resv(bos[i]), fence);
|
||||
}
|
||||
|
||||
lima_gem_unlock_bos(bos, submit->nr_bos, &ctx);
|
||||
drm_gem_unlock_reservations((struct drm_gem_object **)bos,
|
||||
submit->nr_bos, &ctx);
|
||||
|
||||
for (i = 0; i < submit->nr_bos; i++)
|
||||
drm_gem_object_put_unlocked(&bos[i]->gem);
|
||||
drm_gem_object_put_unlocked(&bos[i]->base.base);
|
||||
|
||||
if (out_sync) {
|
||||
drm_syncobj_replace_fence(out_sync, fence);
|
||||
@ -318,13 +264,14 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit)
|
||||
err_out2:
|
||||
lima_sched_task_fini(submit->task);
|
||||
err_out1:
|
||||
lima_gem_unlock_bos(bos, submit->nr_bos, &ctx);
|
||||
drm_gem_unlock_reservations((struct drm_gem_object **)bos,
|
||||
submit->nr_bos, &ctx);
|
||||
err_out0:
|
||||
for (i = 0; i < submit->nr_bos; i++) {
|
||||
if (!bos[i])
|
||||
break;
|
||||
lima_vm_bo_del(vm, bos[i]);
|
||||
drm_gem_object_put_unlocked(&bos[i]->gem);
|
||||
drm_gem_object_put_unlocked(&bos[i]->base.base);
|
||||
}
|
||||
if (out_sync)
|
||||
drm_syncobj_put(out_sync);
|
||||
|
@ -4,19 +4,37 @@
|
||||
#ifndef __LIMA_GEM_H__
|
||||
#define __LIMA_GEM_H__
|
||||
|
||||
struct lima_bo;
|
||||
#include <drm/drm_gem_shmem_helper.h>
|
||||
|
||||
struct lima_submit;
|
||||
|
||||
extern const struct vm_operations_struct lima_gem_vm_ops;
|
||||
struct lima_bo {
|
||||
struct drm_gem_shmem_object base;
|
||||
|
||||
struct lima_bo *lima_gem_create_bo(struct drm_device *dev, u32 size, u32 flags);
|
||||
struct mutex lock;
|
||||
struct list_head va;
|
||||
};
|
||||
|
||||
static inline struct lima_bo *
|
||||
to_lima_bo(struct drm_gem_object *obj)
|
||||
{
|
||||
return container_of(to_drm_gem_shmem_obj(obj), struct lima_bo, base);
|
||||
}
|
||||
|
||||
static inline size_t lima_bo_size(struct lima_bo *bo)
|
||||
{
|
||||
return bo->base.base.size;
|
||||
}
|
||||
|
||||
static inline struct dma_resv *lima_bo_resv(struct lima_bo *bo)
|
||||
{
|
||||
return bo->base.base.resv;
|
||||
}
|
||||
|
||||
struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t size);
|
||||
int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
|
||||
u32 size, u32 flags, u32 *handle);
|
||||
void lima_gem_free_object(struct drm_gem_object *obj);
|
||||
int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file);
|
||||
void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file);
|
||||
int lima_gem_get_info(struct drm_file *file, u32 handle, u32 *va, u64 *offset);
|
||||
int lima_gem_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
int lima_gem_submit(struct drm_file *file, struct lima_submit *submit);
|
||||
int lima_gem_wait(struct drm_file *file, u32 handle, u32 op, s64 timeout_ns);
|
||||
|
||||
|
@ -1,46 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
|
||||
|
||||
#include <linux/dma-buf.h>
|
||||
#include <drm/drm_prime.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_file.h>
|
||||
|
||||
#include "lima_device.h"
|
||||
#include "lima_object.h"
|
||||
#include "lima_gem.h"
|
||||
#include "lima_gem_prime.h"
|
||||
|
||||
struct drm_gem_object *lima_gem_prime_import_sg_table(
|
||||
struct drm_device *dev, struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt)
|
||||
{
|
||||
struct lima_device *ldev = to_lima_dev(dev);
|
||||
struct lima_bo *bo;
|
||||
|
||||
bo = lima_bo_create(ldev, attach->dmabuf->size, 0, sgt);
|
||||
if (IS_ERR(bo))
|
||||
return ERR_CAST(bo);
|
||||
|
||||
return &bo->gem;
|
||||
}
|
||||
|
||||
struct sg_table *lima_gem_prime_get_sg_table(struct drm_gem_object *obj)
|
||||
{
|
||||
struct lima_bo *bo = to_lima_bo(obj);
|
||||
int npages = obj->size >> PAGE_SHIFT;
|
||||
|
||||
return drm_prime_pages_to_sg(bo->pages, npages);
|
||||
}
|
||||
|
||||
int lima_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_mmap_obj(obj, obj->size, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
lima_set_vma_flags(vma);
|
||||
return 0;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
|
||||
|
||||
#ifndef __LIMA_GEM_PRIME_H__
|
||||
#define __LIMA_GEM_PRIME_H__
|
||||
|
||||
struct drm_gem_object *lima_gem_prime_import_sg_table(
|
||||
struct drm_device *dev, struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt);
|
||||
struct sg_table *lima_gem_prime_get_sg_table(struct drm_gem_object *obj);
|
||||
int lima_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
|
||||
|
||||
#endif
|
@ -8,7 +8,6 @@
|
||||
#include "lima_device.h"
|
||||
#include "lima_mmu.h"
|
||||
#include "lima_vm.h"
|
||||
#include "lima_object.h"
|
||||
#include "lima_regs.h"
|
||||
|
||||
#define mmu_write(reg, data) writel(data, ip->iomem + reg)
|
||||
|
@ -1,119 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
|
||||
|
||||
#include <drm/drm_prime.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "lima_object.h"
|
||||
|
||||
void lima_bo_destroy(struct lima_bo *bo)
|
||||
{
|
||||
if (bo->sgt) {
|
||||
kfree(bo->pages);
|
||||
drm_prime_gem_destroy(&bo->gem, bo->sgt);
|
||||
} else {
|
||||
if (bo->pages_dma_addr) {
|
||||
int i, npages = bo->gem.size >> PAGE_SHIFT;
|
||||
|
||||
for (i = 0; i < npages; i++) {
|
||||
if (bo->pages_dma_addr[i])
|
||||
dma_unmap_page(bo->gem.dev->dev,
|
||||
bo->pages_dma_addr[i],
|
||||
PAGE_SIZE, DMA_BIDIRECTIONAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (bo->pages)
|
||||
drm_gem_put_pages(&bo->gem, bo->pages, true, true);
|
||||
}
|
||||
|
||||
kfree(bo->pages_dma_addr);
|
||||
drm_gem_object_release(&bo->gem);
|
||||
kfree(bo);
|
||||
}
|
||||
|
||||
static struct lima_bo *lima_bo_create_struct(struct lima_device *dev, u32 size, u32 flags)
|
||||
{
|
||||
struct lima_bo *bo;
|
||||
int err;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
bo = kzalloc(sizeof(*bo), GFP_KERNEL);
|
||||
if (!bo)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&bo->lock);
|
||||
INIT_LIST_HEAD(&bo->va);
|
||||
|
||||
err = drm_gem_object_init(dev->ddev, &bo->gem, size);
|
||||
if (err) {
|
||||
kfree(bo);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return bo;
|
||||
}
|
||||
|
||||
struct lima_bo *lima_bo_create(struct lima_device *dev, u32 size,
|
||||
u32 flags, struct sg_table *sgt)
|
||||
{
|
||||
int i, err;
|
||||
size_t npages;
|
||||
struct lima_bo *bo, *ret;
|
||||
|
||||
bo = lima_bo_create_struct(dev, size, flags);
|
||||
if (IS_ERR(bo))
|
||||
return bo;
|
||||
|
||||
npages = bo->gem.size >> PAGE_SHIFT;
|
||||
|
||||
bo->pages_dma_addr = kcalloc(npages, sizeof(dma_addr_t), GFP_KERNEL);
|
||||
if (!bo->pages_dma_addr) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (sgt) {
|
||||
bo->sgt = sgt;
|
||||
|
||||
bo->pages = kcalloc(npages, sizeof(*bo->pages), GFP_KERNEL);
|
||||
if (!bo->pages) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = drm_prime_sg_to_page_addr_arrays(
|
||||
sgt, bo->pages, bo->pages_dma_addr, npages);
|
||||
if (err) {
|
||||
ret = ERR_PTR(err);
|
||||
goto err_out;
|
||||
}
|
||||
} else {
|
||||
mapping_set_gfp_mask(bo->gem.filp->f_mapping, GFP_DMA32);
|
||||
bo->pages = drm_gem_get_pages(&bo->gem);
|
||||
if (IS_ERR(bo->pages)) {
|
||||
ret = ERR_CAST(bo->pages);
|
||||
bo->pages = NULL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
for (i = 0; i < npages; i++) {
|
||||
dma_addr_t addr = dma_map_page(dev->dev, bo->pages[i], 0,
|
||||
PAGE_SIZE, DMA_BIDIRECTIONAL);
|
||||
if (dma_mapping_error(dev->dev, addr)) {
|
||||
ret = ERR_PTR(-EFAULT);
|
||||
goto err_out;
|
||||
}
|
||||
bo->pages_dma_addr[i] = addr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return bo;
|
||||
|
||||
err_out:
|
||||
lima_bo_destroy(bo);
|
||||
return ret;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/* Copyright 2018-2019 Qiang Yu <yuq825@gmail.com> */
|
||||
|
||||
#ifndef __LIMA_OBJECT_H__
|
||||
#define __LIMA_OBJECT_H__
|
||||
|
||||
#include <drm/drm_gem.h>
|
||||
|
||||
#include "lima_device.h"
|
||||
|
||||
struct lima_bo {
|
||||
struct drm_gem_object gem;
|
||||
|
||||
struct page **pages;
|
||||
dma_addr_t *pages_dma_addr;
|
||||
struct sg_table *sgt;
|
||||
void *vaddr;
|
||||
|
||||
struct mutex lock;
|
||||
struct list_head va;
|
||||
};
|
||||
|
||||
static inline struct lima_bo *
|
||||
to_lima_bo(struct drm_gem_object *obj)
|
||||
{
|
||||
return container_of(obj, struct lima_bo, gem);
|
||||
}
|
||||
|
||||
struct lima_bo *lima_bo_create(struct lima_device *dev, u32 size,
|
||||
u32 flags, struct sg_table *sgt);
|
||||
void lima_bo_destroy(struct lima_bo *bo);
|
||||
void *lima_bo_vmap(struct lima_bo *bo);
|
||||
void lima_bo_vunmap(struct lima_bo *bo);
|
||||
|
||||
#endif
|
@ -10,7 +10,7 @@
|
||||
#include "lima_vm.h"
|
||||
#include "lima_mmu.h"
|
||||
#include "lima_l2_cache.h"
|
||||
#include "lima_object.h"
|
||||
#include "lima_gem.h"
|
||||
|
||||
struct lima_fence {
|
||||
struct dma_fence base;
|
||||
@ -117,7 +117,7 @@ int lima_sched_task_init(struct lima_sched_task *task,
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_bos; i++)
|
||||
drm_gem_object_get(&bos[i]->gem);
|
||||
drm_gem_object_get(&bos[i]->base.base);
|
||||
|
||||
err = drm_sched_job_init(&task->base, &context->base, vm);
|
||||
if (err) {
|
||||
@ -148,7 +148,7 @@ void lima_sched_task_fini(struct lima_sched_task *task)
|
||||
|
||||
if (task->bos) {
|
||||
for (i = 0; i < task->num_bos; i++)
|
||||
drm_gem_object_put_unlocked(&task->bos[i]->gem);
|
||||
drm_gem_object_put_unlocked(&task->bos[i]->base.base);
|
||||
kfree(task->bos);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include "lima_device.h"
|
||||
#include "lima_vm.h"
|
||||
#include "lima_object.h"
|
||||
#include "lima_gem.h"
|
||||
#include "lima_regs.h"
|
||||
|
||||
struct lima_bo_va {
|
||||
@ -32,7 +32,7 @@ struct lima_bo_va {
|
||||
#define LIMA_BTE(va) ((va & LIMA_VM_BT_MASK) >> LIMA_VM_BT_SHIFT)
|
||||
|
||||
|
||||
static void lima_vm_unmap_page_table(struct lima_vm *vm, u32 start, u32 end)
|
||||
static void lima_vm_unmap_range(struct lima_vm *vm, u32 start, u32 end)
|
||||
{
|
||||
u32 addr;
|
||||
|
||||
@ -44,41 +44,32 @@ static void lima_vm_unmap_page_table(struct lima_vm *vm, u32 start, u32 end)
|
||||
}
|
||||
}
|
||||
|
||||
static int lima_vm_map_page_table(struct lima_vm *vm, dma_addr_t *dma,
|
||||
u32 start, u32 end)
|
||||
static int lima_vm_map_page(struct lima_vm *vm, dma_addr_t pa, u32 va)
|
||||
{
|
||||
u64 addr;
|
||||
int i = 0;
|
||||
u32 pbe = LIMA_PBE(va);
|
||||
u32 bte = LIMA_BTE(va);
|
||||
|
||||
for (addr = start; addr <= end; addr += LIMA_PAGE_SIZE) {
|
||||
u32 pbe = LIMA_PBE(addr);
|
||||
u32 bte = LIMA_BTE(addr);
|
||||
if (!vm->bts[pbe].cpu) {
|
||||
dma_addr_t pts;
|
||||
u32 *pd;
|
||||
int j;
|
||||
|
||||
if (!vm->bts[pbe].cpu) {
|
||||
dma_addr_t pts;
|
||||
u32 *pd;
|
||||
int j;
|
||||
vm->bts[pbe].cpu = dma_alloc_wc(
|
||||
vm->dev->dev, LIMA_PAGE_SIZE << LIMA_VM_NUM_PT_PER_BT_SHIFT,
|
||||
&vm->bts[pbe].dma, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO);
|
||||
if (!vm->bts[pbe].cpu)
|
||||
return -ENOMEM;
|
||||
|
||||
vm->bts[pbe].cpu = dma_alloc_wc(
|
||||
vm->dev->dev, LIMA_PAGE_SIZE << LIMA_VM_NUM_PT_PER_BT_SHIFT,
|
||||
&vm->bts[pbe].dma, GFP_KERNEL | __GFP_ZERO);
|
||||
if (!vm->bts[pbe].cpu) {
|
||||
if (addr != start)
|
||||
lima_vm_unmap_page_table(vm, start, addr - 1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pts = vm->bts[pbe].dma;
|
||||
pd = vm->pd.cpu + (pbe << LIMA_VM_NUM_PT_PER_BT_SHIFT);
|
||||
for (j = 0; j < LIMA_VM_NUM_PT_PER_BT; j++) {
|
||||
pd[j] = pts | LIMA_VM_FLAG_PRESENT;
|
||||
pts += LIMA_PAGE_SIZE;
|
||||
}
|
||||
pts = vm->bts[pbe].dma;
|
||||
pd = vm->pd.cpu + (pbe << LIMA_VM_NUM_PT_PER_BT_SHIFT);
|
||||
for (j = 0; j < LIMA_VM_NUM_PT_PER_BT; j++) {
|
||||
pd[j] = pts | LIMA_VM_FLAG_PRESENT;
|
||||
pts += LIMA_PAGE_SIZE;
|
||||
}
|
||||
|
||||
vm->bts[pbe].cpu[bte] = dma[i++] | LIMA_VM_FLAGS_CACHE;
|
||||
}
|
||||
|
||||
vm->bts[pbe].cpu[bte] = pa | LIMA_VM_FLAGS_CACHE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -100,7 +91,8 @@ lima_vm_bo_find(struct lima_vm *vm, struct lima_bo *bo)
|
||||
int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo, bool create)
|
||||
{
|
||||
struct lima_bo_va *bo_va;
|
||||
int err;
|
||||
struct sg_dma_page_iter sg_iter;
|
||||
int offset = 0, err;
|
||||
|
||||
mutex_lock(&bo->lock);
|
||||
|
||||
@ -128,14 +120,18 @@ int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo, bool create)
|
||||
|
||||
mutex_lock(&vm->lock);
|
||||
|
||||
err = drm_mm_insert_node(&vm->mm, &bo_va->node, bo->gem.size);
|
||||
err = drm_mm_insert_node(&vm->mm, &bo_va->node, lima_bo_size(bo));
|
||||
if (err)
|
||||
goto err_out1;
|
||||
|
||||
err = lima_vm_map_page_table(vm, bo->pages_dma_addr, bo_va->node.start,
|
||||
bo_va->node.start + bo_va->node.size - 1);
|
||||
if (err)
|
||||
goto err_out2;
|
||||
for_each_sg_dma_page(bo->base.sgt->sgl, &sg_iter, bo->base.sgt->nents, 0) {
|
||||
err = lima_vm_map_page(vm, sg_page_iter_dma_address(&sg_iter),
|
||||
bo_va->node.start + offset);
|
||||
if (err)
|
||||
goto err_out2;
|
||||
|
||||
offset += PAGE_SIZE;
|
||||
}
|
||||
|
||||
mutex_unlock(&vm->lock);
|
||||
|
||||
@ -145,6 +141,8 @@ int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo, bool create)
|
||||
return 0;
|
||||
|
||||
err_out2:
|
||||
if (offset)
|
||||
lima_vm_unmap_range(vm, bo_va->node.start, bo_va->node.start + offset - 1);
|
||||
drm_mm_remove_node(&bo_va->node);
|
||||
err_out1:
|
||||
mutex_unlock(&vm->lock);
|
||||
@ -168,8 +166,8 @@ void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo)
|
||||
|
||||
mutex_lock(&vm->lock);
|
||||
|
||||
lima_vm_unmap_page_table(vm, bo_va->node.start,
|
||||
bo_va->node.start + bo_va->node.size - 1);
|
||||
lima_vm_unmap_range(vm, bo_va->node.start,
|
||||
bo_va->node.start + bo_va->node.size - 1);
|
||||
|
||||
drm_mm_remove_node(&bo_va->node);
|
||||
|
||||
@ -210,14 +208,13 @@ struct lima_vm *lima_vm_create(struct lima_device *dev)
|
||||
kref_init(&vm->refcount);
|
||||
|
||||
vm->pd.cpu = dma_alloc_wc(dev->dev, LIMA_PAGE_SIZE, &vm->pd.dma,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO);
|
||||
if (!vm->pd.cpu)
|
||||
goto err_out0;
|
||||
|
||||
if (dev->dlbu_cpu) {
|
||||
int err = lima_vm_map_page_table(
|
||||
vm, &dev->dlbu_dma, LIMA_VA_RESERVE_DLBU,
|
||||
LIMA_VA_RESERVE_DLBU + LIMA_PAGE_SIZE - 1);
|
||||
int err = lima_vm_map_page(
|
||||
vm, dev->dlbu_dma, LIMA_VA_RESERVE_DLBU);
|
||||
if (err)
|
||||
goto err_out1;
|
||||
}
|
||||
|
@ -977,6 +977,11 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
|
||||
dw_plat_data->input_bus_format = MEDIA_BUS_FMT_YUV8_1X24;
|
||||
dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
|
||||
|
||||
if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
|
||||
dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
|
||||
dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
|
||||
dw_plat_data->use_drm_infoframe = true;
|
||||
|
||||
platform_set_drvdata(pdev, meson_dw_hdmi);
|
||||
|
||||
meson_dw_hdmi->hdmi = dw_hdmi_bind(pdev, encoder,
|
||||
|
@ -58,10 +58,7 @@ static void mga_pci_remove(struct pci_dev *pdev)
|
||||
drm_put_dev(dev);
|
||||
}
|
||||
|
||||
static const struct file_operations mgag200_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_VRAM_MM_FILE_OPERATIONS
|
||||
};
|
||||
DEFINE_DRM_GEM_FOPS(mgag200_driver_fops);
|
||||
|
||||
static struct drm_driver driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET,
|
||||
|
@ -89,7 +89,6 @@ struct edp_ctrl {
|
||||
/* edid raw data */
|
||||
struct edid *edid;
|
||||
|
||||
struct drm_dp_link dp_link;
|
||||
struct drm_dp_aux *drm_aux;
|
||||
|
||||
/* dpcd raw data */
|
||||
@ -403,7 +402,7 @@ static void edp_fill_link_cfg(struct edp_ctrl *ctrl)
|
||||
u32 prate;
|
||||
u32 lrate;
|
||||
u32 bpp;
|
||||
u8 max_lane = ctrl->dp_link.num_lanes;
|
||||
u8 max_lane = drm_dp_max_lane_count(ctrl->dpcd);
|
||||
u8 lane;
|
||||
|
||||
prate = ctrl->pixel_rate;
|
||||
@ -413,7 +412,7 @@ static void edp_fill_link_cfg(struct edp_ctrl *ctrl)
|
||||
* By default, use the maximum link rate and minimum lane count,
|
||||
* so that we can do rate down shift during link training.
|
||||
*/
|
||||
ctrl->link_rate = drm_dp_link_rate_to_bw_code(ctrl->dp_link.rate);
|
||||
ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
|
||||
|
||||
prate *= bpp;
|
||||
prate /= 8; /* in kByte */
|
||||
@ -439,7 +438,7 @@ static void edp_config_ctrl(struct edp_ctrl *ctrl)
|
||||
|
||||
data = EDP_CONFIGURATION_CTRL_LANES(ctrl->lane_cnt - 1);
|
||||
|
||||
if (ctrl->dp_link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
|
||||
if (drm_dp_enhanced_frame_cap(ctrl->dpcd))
|
||||
data |= EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING;
|
||||
|
||||
depth = EDP_6BIT;
|
||||
@ -701,7 +700,7 @@ static int edp_link_rate_down_shift(struct edp_ctrl *ctrl)
|
||||
|
||||
rate = ctrl->link_rate;
|
||||
lane = ctrl->lane_cnt;
|
||||
max_lane = ctrl->dp_link.num_lanes;
|
||||
max_lane = drm_dp_max_lane_count(ctrl->dpcd);
|
||||
|
||||
bpp = ctrl->color_depth * 3;
|
||||
prate = ctrl->pixel_rate;
|
||||
@ -751,18 +750,22 @@ static int edp_clear_training_pattern(struct edp_ctrl *ctrl)
|
||||
|
||||
static int edp_do_link_train(struct edp_ctrl *ctrl)
|
||||
{
|
||||
u8 values[2];
|
||||
int ret;
|
||||
struct drm_dp_link dp_link;
|
||||
|
||||
DBG("");
|
||||
/*
|
||||
* Set the current link rate and lane cnt to panel. They may have been
|
||||
* adjusted and the values are different from them in DPCD CAP
|
||||
*/
|
||||
dp_link.num_lanes = ctrl->lane_cnt;
|
||||
dp_link.rate = drm_dp_bw_code_to_link_rate(ctrl->link_rate);
|
||||
dp_link.capabilities = ctrl->dp_link.capabilities;
|
||||
if (drm_dp_link_configure(ctrl->drm_aux, &dp_link) < 0)
|
||||
values[0] = ctrl->lane_cnt;
|
||||
values[1] = ctrl->link_rate;
|
||||
|
||||
if (drm_dp_enhanced_frame_cap(ctrl->dpcd))
|
||||
values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
|
||||
|
||||
if (drm_dp_dpcd_write(ctrl->drm_aux, DP_LINK_BW_SET, values,
|
||||
sizeof(values)) < 0)
|
||||
return EDP_TRAIN_FAIL;
|
||||
|
||||
ctrl->v_level = 0; /* start from default level */
|
||||
@ -952,6 +955,7 @@ static void edp_ctrl_on_worker(struct work_struct *work)
|
||||
{
|
||||
struct edp_ctrl *ctrl = container_of(
|
||||
work, struct edp_ctrl, on_work);
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ctrl->dev_mutex);
|
||||
@ -965,9 +969,27 @@ static void edp_ctrl_on_worker(struct work_struct *work)
|
||||
edp_ctrl_link_enable(ctrl, 1);
|
||||
|
||||
edp_ctrl_irq_enable(ctrl, 1);
|
||||
ret = drm_dp_link_power_up(ctrl->drm_aux, &ctrl->dp_link);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* DP_SET_POWER register is only available on DPCD v1.1 and later */
|
||||
if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
|
||||
ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
value &= ~DP_SET_POWER_MASK;
|
||||
value |= DP_SET_POWER_D0;
|
||||
|
||||
ret = drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* According to the DP 1.1 specification, a "Sink Device must
|
||||
* exit the power saving state within 1 ms" (Section 2.5.3.1,
|
||||
* Table 5-52, "Sink Control Field" (register 0x600).
|
||||
*/
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
ctrl->power_on = true;
|
||||
|
||||
@ -1011,7 +1033,19 @@ static void edp_ctrl_off_worker(struct work_struct *work)
|
||||
|
||||
edp_state_ctrl(ctrl, 0);
|
||||
|
||||
drm_dp_link_power_down(ctrl->drm_aux, &ctrl->dp_link);
|
||||
/* DP_SET_POWER register is only available on DPCD v1.1 and later */
|
||||
if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
|
||||
if (ret > 0) {
|
||||
value &= ~DP_SET_POWER_MASK;
|
||||
value |= DP_SET_POWER_D3;
|
||||
|
||||
drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
|
||||
}
|
||||
}
|
||||
|
||||
edp_ctrl_irq_enable(ctrl, 0);
|
||||
|
||||
@ -1225,14 +1259,8 @@ int msm_edp_ctrl_get_panel_info(struct edp_ctrl *ctrl,
|
||||
edp_ctrl_irq_enable(ctrl, 1);
|
||||
}
|
||||
|
||||
ret = drm_dp_link_probe(ctrl->drm_aux, &ctrl->dp_link);
|
||||
if (ret) {
|
||||
pr_err("%s: read dpcd cap failed, %d\n", __func__, ret);
|
||||
goto disable_ret;
|
||||
}
|
||||
|
||||
/* Initialize link rate as panel max link rate */
|
||||
ctrl->link_rate = drm_dp_link_rate_to_bw_code(ctrl->dp_link.rate);
|
||||
ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
|
||||
|
||||
ctrl->edid = drm_get_edid(connector, &ctrl->drm_aux->ddc);
|
||||
if (!ctrl->edid) {
|
||||
|
@ -95,8 +95,11 @@ static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb)
|
||||
|
||||
reg = readl(mxsfb->base + LCDC_CTRL);
|
||||
|
||||
if (mxsfb->connector.display_info.num_bus_formats)
|
||||
bus_format = mxsfb->connector.display_info.bus_formats[0];
|
||||
if (mxsfb->connector->display_info.num_bus_formats)
|
||||
bus_format = mxsfb->connector->display_info.bus_formats[0];
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
|
||||
bus_format);
|
||||
|
||||
reg &= ~CTRL_BUS_WIDTH_MASK;
|
||||
switch (bus_format) {
|
||||
@ -204,8 +207,9 @@ static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb)
|
||||
|
||||
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
|
||||
{
|
||||
struct drm_device *drm = mxsfb->pipe.crtc.dev;
|
||||
struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
|
||||
const u32 bus_flags = mxsfb->connector.display_info.bus_flags;
|
||||
u32 bus_flags = mxsfb->connector->display_info.bus_flags;
|
||||
u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
|
||||
int err;
|
||||
|
||||
@ -229,6 +233,16 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
|
||||
|
||||
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
|
||||
|
||||
if (mxsfb->bridge && mxsfb->bridge->timings)
|
||||
bus_flags = mxsfb->bridge->timings->input_bus_flags;
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
|
||||
m->crtc_clock,
|
||||
(int)(clk_get_rate(mxsfb->clk) / 1000));
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
|
||||
bus_flags);
|
||||
DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
|
||||
|
||||
writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |
|
||||
TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),
|
||||
mxsfb->base + mxsfb->devdata->transfer_count);
|
||||
|
@ -101,9 +101,25 @@ static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
|
||||
struct drm_device *drm = pipe->plane.dev;
|
||||
|
||||
if (!mxsfb->connector) {
|
||||
list_for_each_entry(connector,
|
||||
&drm->mode_config.connector_list,
|
||||
head)
|
||||
if (connector->encoder == &mxsfb->pipe.encoder) {
|
||||
mxsfb->connector = connector;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mxsfb->connector) {
|
||||
dev_warn(drm->dev, "No connector attached, using default\n");
|
||||
mxsfb->connector = &mxsfb->panel_connector;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(drm->dev);
|
||||
drm_panel_prepare(mxsfb->panel);
|
||||
mxsfb_crtc_enable(mxsfb);
|
||||
@ -129,6 +145,9 @@ static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe)
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
}
|
||||
spin_unlock_irq(&drm->event_lock);
|
||||
|
||||
if (mxsfb->connector != &mxsfb->panel_connector)
|
||||
mxsfb->connector = NULL;
|
||||
}
|
||||
|
||||
static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe,
|
||||
@ -226,16 +245,33 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
|
||||
|
||||
ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
|
||||
mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL,
|
||||
&mxsfb->connector);
|
||||
mxsfb->connector);
|
||||
if (ret < 0) {
|
||||
dev_err(drm->dev, "Cannot setup simple display pipe\n");
|
||||
goto err_vblank;
|
||||
}
|
||||
|
||||
ret = drm_panel_attach(mxsfb->panel, &mxsfb->connector);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Cannot connect panel\n");
|
||||
goto err_vblank;
|
||||
/*
|
||||
* Attach panel only if there is one.
|
||||
* If there is no panel attach, it must be a bridge. In this case, we
|
||||
* need a reference to its connector for a proper initialization.
|
||||
* We will do this check in pipe->enable(), since the connector won't
|
||||
* be attached to an encoder until then.
|
||||
*/
|
||||
|
||||
if (mxsfb->panel) {
|
||||
ret = drm_panel_attach(mxsfb->panel, mxsfb->connector);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Cannot connect panel: %d\n", ret);
|
||||
goto err_vblank;
|
||||
}
|
||||
} else if (mxsfb->bridge) {
|
||||
ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
|
||||
mxsfb->bridge);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
|
||||
goto err_vblank;
|
||||
}
|
||||
}
|
||||
|
||||
drm->mode_config.min_width = MXSFB_MIN_XRES;
|
||||
|
@ -27,8 +27,10 @@ struct mxsfb_drm_private {
|
||||
struct clk *clk_disp_axi;
|
||||
|
||||
struct drm_simple_display_pipe pipe;
|
||||
struct drm_connector connector;
|
||||
struct drm_connector panel_connector;
|
||||
struct drm_connector *connector;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
};
|
||||
|
||||
int mxsfb_setup_crtc(struct drm_device *dev);
|
||||
|
@ -21,7 +21,8 @@
|
||||
static struct mxsfb_drm_private *
|
||||
drm_connector_to_mxsfb_drm_private(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct mxsfb_drm_private, connector);
|
||||
return container_of(connector, struct mxsfb_drm_private,
|
||||
panel_connector);
|
||||
}
|
||||
|
||||
static int mxsfb_panel_get_modes(struct drm_connector *connector)
|
||||
@ -76,22 +77,23 @@ static const struct drm_connector_funcs mxsfb_panel_connector_funcs = {
|
||||
int mxsfb_create_output(struct drm_device *drm)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
struct drm_panel *panel;
|
||||
int ret;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel, NULL);
|
||||
ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0,
|
||||
&mxsfb->panel, &mxsfb->bridge);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mxsfb->connector.dpms = DRM_MODE_DPMS_OFF;
|
||||
mxsfb->connector.polled = 0;
|
||||
drm_connector_helper_add(&mxsfb->connector,
|
||||
&mxsfb_panel_connector_helper_funcs);
|
||||
ret = drm_connector_init(drm, &mxsfb->connector,
|
||||
&mxsfb_panel_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_Unknown);
|
||||
if (!ret)
|
||||
mxsfb->panel = panel;
|
||||
if (mxsfb->panel) {
|
||||
mxsfb->connector = &mxsfb->panel_connector;
|
||||
mxsfb->connector->dpms = DRM_MODE_DPMS_OFF;
|
||||
mxsfb->connector->polled = 0;
|
||||
drm_connector_helper_add(mxsfb->connector,
|
||||
&mxsfb_panel_connector_helper_funcs);
|
||||
ret = drm_connector_init(drm, mxsfb->connector,
|
||||
&mxsfb_panel_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_Unknown);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -3548,7 +3548,7 @@ static int dsi_proto_config(struct dsi_data *dsi)
|
||||
|
||||
static void dsi_proto_timings(struct dsi_data *dsi)
|
||||
{
|
||||
unsigned int tlpx, tclk_zero, tclk_prepare, tclk_trail;
|
||||
unsigned int tlpx, tclk_zero, tclk_prepare;
|
||||
unsigned int tclk_pre, tclk_post;
|
||||
unsigned int ths_prepare, ths_prepare_ths_zero, ths_zero;
|
||||
unsigned int ths_trail, ths_exit;
|
||||
@ -3567,7 +3567,6 @@ static void dsi_proto_timings(struct dsi_data *dsi)
|
||||
|
||||
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1);
|
||||
tlpx = FLD_GET(r, 20, 16) * 2;
|
||||
tclk_trail = FLD_GET(r, 15, 8);
|
||||
tclk_zero = FLD_GET(r, 7, 0);
|
||||
|
||||
r = dsi_read_reg(dsi, DSI_DSIPHY_CFG2);
|
||||
|
@ -676,7 +676,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
|
||||
struct hdmi_audio_format audio_format;
|
||||
struct hdmi_audio_dma audio_dma;
|
||||
struct hdmi_core_audio_config acore;
|
||||
int err, n, cts, channel_count;
|
||||
int n, cts, channel_count;
|
||||
unsigned int fs_nr;
|
||||
bool word_length_16b = false;
|
||||
|
||||
@ -738,7 +738,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = hdmi_compute_acr(pclk, fs_nr, &n, &cts);
|
||||
hdmi_compute_acr(pclk, fs_nr, &n, &cts);
|
||||
|
||||
/* Audio clock regeneration settings */
|
||||
acore.n = n;
|
||||
|
@ -807,7 +807,7 @@ int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
|
||||
struct hdmi_audio_format audio_format;
|
||||
struct hdmi_audio_dma audio_dma;
|
||||
struct hdmi_core_audio_config core_cfg;
|
||||
int err, n, cts, channel_count;
|
||||
int n, cts, channel_count;
|
||||
unsigned int fs_nr;
|
||||
bool word_length_16b = false;
|
||||
|
||||
@ -850,7 +850,7 @@ int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = hdmi_compute_acr(pclk, fs_nr, &n, &cts);
|
||||
hdmi_compute_acr(pclk, fs_nr, &n, &cts);
|
||||
core_cfg.n = n;
|
||||
core_cfg.cts = cts;
|
||||
|
||||
|
@ -113,7 +113,7 @@ extern struct platform_driver omap_dmm_driver;
|
||||
/* GEM bo flags -> tiler fmt */
|
||||
static inline enum tiler_fmt gem2fmt(u32 flags)
|
||||
{
|
||||
switch (flags & OMAP_BO_TILED) {
|
||||
switch (flags & OMAP_BO_TILED_MASK) {
|
||||
case OMAP_BO_TILED_8:
|
||||
return TILFMT_8BIT;
|
||||
case OMAP_BO_TILED_16:
|
||||
|
@ -95,7 +95,7 @@ static u32 get_linear_addr(struct drm_framebuffer *fb,
|
||||
|
||||
bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb)
|
||||
{
|
||||
return omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED;
|
||||
return omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED_MASK;
|
||||
}
|
||||
|
||||
/* Note: DRM rotates counter-clockwise, TILER & DSS rotates clockwise */
|
||||
@ -135,7 +135,6 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
|
||||
{
|
||||
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
|
||||
const struct drm_format_info *format = omap_fb->format;
|
||||
struct plane *plane = &omap_fb->planes[0];
|
||||
u32 x, y, orient = 0;
|
||||
|
||||
info->fourcc = fb->format->format;
|
||||
@ -154,7 +153,7 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
|
||||
x = state->src_x >> 16;
|
||||
y = state->src_y >> 16;
|
||||
|
||||
if (omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED) {
|
||||
if (omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED_MASK) {
|
||||
u32 w = state->src_w >> 16;
|
||||
u32 h = state->src_h >> 16;
|
||||
|
||||
@ -209,10 +208,8 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
|
||||
info->screen_width /= format->cpp[0];
|
||||
|
||||
if (fb->format->format == DRM_FORMAT_NV12) {
|
||||
plane = &omap_fb->planes[1];
|
||||
|
||||
if (info->rotation_type == OMAP_DSS_ROT_TILER) {
|
||||
WARN_ON(!(omap_gem_flags(fb->obj[1]) & OMAP_BO_TILED));
|
||||
WARN_ON(!(omap_gem_flags(fb->obj[1]) & OMAP_BO_TILED_MASK));
|
||||
omap_gem_rotated_dma_addr(fb->obj[1], orient, x/2, y/2,
|
||||
&info->p_uv_addr);
|
||||
} else {
|
||||
|
@ -67,7 +67,7 @@ struct omap_gem_object {
|
||||
/**
|
||||
* # of users of dma_addr
|
||||
*/
|
||||
u32 dma_addr_cnt;
|
||||
refcount_t dma_addr_cnt;
|
||||
|
||||
/**
|
||||
* If the buffer has been imported from a dmabuf the OMAP_DB_DMABUF flag
|
||||
@ -196,7 +196,7 @@ static void omap_gem_evict(struct drm_gem_object *obj)
|
||||
struct omap_gem_object *omap_obj = to_omap_bo(obj);
|
||||
struct omap_drm_private *priv = obj->dev->dev_private;
|
||||
|
||||
if (omap_obj->flags & OMAP_BO_TILED) {
|
||||
if (omap_obj->flags & OMAP_BO_TILED_MASK) {
|
||||
enum tiler_fmt fmt = gem2fmt(omap_obj->flags);
|
||||
int i;
|
||||
|
||||
@ -324,7 +324,7 @@ size_t omap_gem_mmap_size(struct drm_gem_object *obj)
|
||||
struct omap_gem_object *omap_obj = to_omap_bo(obj);
|
||||
size_t size = obj->size;
|
||||
|
||||
if (omap_obj->flags & OMAP_BO_TILED) {
|
||||
if (omap_obj->flags & OMAP_BO_TILED_MASK) {
|
||||
/* for tiled buffers, the virtual size has stride rounded up
|
||||
* to 4kb.. (to hide the fact that row n+1 might start 16kb or
|
||||
* 32kb later!). But we don't back the entire buffer with
|
||||
@ -513,7 +513,7 @@ vm_fault_t omap_gem_fault(struct vm_fault *vmf)
|
||||
* probably trigger put_pages()?
|
||||
*/
|
||||
|
||||
if (omap_obj->flags & OMAP_BO_TILED)
|
||||
if (omap_obj->flags & OMAP_BO_TILED_MASK)
|
||||
ret = omap_gem_fault_2d(obj, vma, vmf);
|
||||
else
|
||||
ret = omap_gem_fault_1d(obj, vma, vmf);
|
||||
@ -773,18 +773,20 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr)
|
||||
mutex_lock(&omap_obj->lock);
|
||||
|
||||
if (!omap_gem_is_contiguous(omap_obj) && priv->has_dmm) {
|
||||
if (omap_obj->dma_addr_cnt == 0) {
|
||||
if (refcount_read(&omap_obj->dma_addr_cnt) == 0) {
|
||||
u32 npages = obj->size >> PAGE_SHIFT;
|
||||
enum tiler_fmt fmt = gem2fmt(omap_obj->flags);
|
||||
struct tiler_block *block;
|
||||
|
||||
BUG_ON(omap_obj->block);
|
||||
|
||||
refcount_set(&omap_obj->dma_addr_cnt, 1);
|
||||
|
||||
ret = omap_gem_attach_pages(obj);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
if (omap_obj->flags & OMAP_BO_TILED) {
|
||||
if (omap_obj->flags & OMAP_BO_TILED_MASK) {
|
||||
block = tiler_reserve_2d(fmt,
|
||||
omap_obj->width,
|
||||
omap_obj->height, 0);
|
||||
@ -813,13 +815,15 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr)
|
||||
omap_obj->block = block;
|
||||
|
||||
DBG("got dma address: %pad", &omap_obj->dma_addr);
|
||||
} else {
|
||||
refcount_inc(&omap_obj->dma_addr_cnt);
|
||||
}
|
||||
|
||||
omap_obj->dma_addr_cnt++;
|
||||
|
||||
*dma_addr = omap_obj->dma_addr;
|
||||
if (dma_addr)
|
||||
*dma_addr = omap_obj->dma_addr;
|
||||
} else if (omap_gem_is_contiguous(omap_obj)) {
|
||||
*dma_addr = omap_obj->dma_addr;
|
||||
if (dma_addr)
|
||||
*dma_addr = omap_obj->dma_addr;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
@ -831,39 +835,47 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_gem_unpin_locked() - Unpin a GEM object from memory
|
||||
* @obj: the GEM object
|
||||
*
|
||||
* omap_gem_unpin() without locking.
|
||||
*/
|
||||
static void omap_gem_unpin_locked(struct drm_gem_object *obj)
|
||||
{
|
||||
struct omap_gem_object *omap_obj = to_omap_bo(obj);
|
||||
int ret;
|
||||
|
||||
if (refcount_dec_and_test(&omap_obj->dma_addr_cnt)) {
|
||||
ret = tiler_unpin(omap_obj->block);
|
||||
if (ret) {
|
||||
dev_err(obj->dev->dev,
|
||||
"could not unpin pages: %d\n", ret);
|
||||
}
|
||||
ret = tiler_release(omap_obj->block);
|
||||
if (ret) {
|
||||
dev_err(obj->dev->dev,
|
||||
"could not release unmap: %d\n", ret);
|
||||
}
|
||||
omap_obj->dma_addr = 0;
|
||||
omap_obj->block = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_gem_unpin() - Unpin a GEM object from memory
|
||||
* @obj: the GEM object
|
||||
*
|
||||
* Unpin the given GEM object previously pinned with omap_gem_pin(). Pins are
|
||||
* reference-counted, the actualy unpin will only be performed when the number
|
||||
* reference-counted, the actual unpin will only be performed when the number
|
||||
* of calls to this function matches the number of calls to omap_gem_pin().
|
||||
*/
|
||||
void omap_gem_unpin(struct drm_gem_object *obj)
|
||||
{
|
||||
struct omap_gem_object *omap_obj = to_omap_bo(obj);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&omap_obj->lock);
|
||||
|
||||
if (omap_obj->dma_addr_cnt > 0) {
|
||||
omap_obj->dma_addr_cnt--;
|
||||
if (omap_obj->dma_addr_cnt == 0) {
|
||||
ret = tiler_unpin(omap_obj->block);
|
||||
if (ret) {
|
||||
dev_err(obj->dev->dev,
|
||||
"could not unpin pages: %d\n", ret);
|
||||
}
|
||||
ret = tiler_release(omap_obj->block);
|
||||
if (ret) {
|
||||
dev_err(obj->dev->dev,
|
||||
"could not release unmap: %d\n", ret);
|
||||
}
|
||||
omap_obj->dma_addr = 0;
|
||||
omap_obj->block = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
omap_gem_unpin_locked(obj);
|
||||
mutex_unlock(&omap_obj->lock);
|
||||
}
|
||||
|
||||
@ -879,8 +891,8 @@ int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, u32 orient,
|
||||
|
||||
mutex_lock(&omap_obj->lock);
|
||||
|
||||
if ((omap_obj->dma_addr_cnt > 0) && omap_obj->block &&
|
||||
(omap_obj->flags & OMAP_BO_TILED)) {
|
||||
if ((refcount_read(&omap_obj->dma_addr_cnt) > 0) && omap_obj->block &&
|
||||
(omap_obj->flags & OMAP_BO_TILED_MASK)) {
|
||||
*dma_addr = tiler_tsptr(omap_obj->block, orient, x, y);
|
||||
ret = 0;
|
||||
}
|
||||
@ -895,7 +907,7 @@ int omap_gem_tiled_stride(struct drm_gem_object *obj, u32 orient)
|
||||
{
|
||||
struct omap_gem_object *omap_obj = to_omap_bo(obj);
|
||||
int ret = -EINVAL;
|
||||
if (omap_obj->flags & OMAP_BO_TILED)
|
||||
if (omap_obj->flags & OMAP_BO_TILED_MASK)
|
||||
ret = tiler_stride(gem2fmt(omap_obj->flags), orient);
|
||||
return ret;
|
||||
}
|
||||
@ -1030,10 +1042,11 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
|
||||
|
||||
seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d",
|
||||
omap_obj->flags, obj->name, kref_read(&obj->refcount),
|
||||
off, &omap_obj->dma_addr, omap_obj->dma_addr_cnt,
|
||||
off, &omap_obj->dma_addr,
|
||||
refcount_read(&omap_obj->dma_addr_cnt),
|
||||
omap_obj->vaddr, omap_obj->roll);
|
||||
|
||||
if (omap_obj->flags & OMAP_BO_TILED) {
|
||||
if (omap_obj->flags & OMAP_BO_TILED_MASK) {
|
||||
seq_printf(m, " %dx%d", omap_obj->width, omap_obj->height);
|
||||
if (omap_obj->block) {
|
||||
struct tcm_area *area = &omap_obj->block->area;
|
||||
@ -1093,7 +1106,7 @@ void omap_gem_free_object(struct drm_gem_object *obj)
|
||||
mutex_lock(&omap_obj->lock);
|
||||
|
||||
/* The object should not be pinned. */
|
||||
WARN_ON(omap_obj->dma_addr_cnt > 0);
|
||||
WARN_ON(refcount_read(&omap_obj->dma_addr_cnt) > 0);
|
||||
|
||||
if (omap_obj->pages) {
|
||||
if (omap_obj->flags & OMAP_BO_MEM_DMABUF)
|
||||
@ -1120,6 +1133,38 @@ void omap_gem_free_object(struct drm_gem_object *obj)
|
||||
kfree(omap_obj);
|
||||
}
|
||||
|
||||
static bool omap_gem_validate_flags(struct drm_device *dev, u32 flags)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
|
||||
switch (flags & OMAP_BO_CACHE_MASK) {
|
||||
case OMAP_BO_CACHED:
|
||||
case OMAP_BO_WC:
|
||||
case OMAP_BO_CACHE_MASK:
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags & OMAP_BO_TILED_MASK) {
|
||||
if (!priv->usergart)
|
||||
return false;
|
||||
|
||||
switch (flags & OMAP_BO_TILED_MASK) {
|
||||
case OMAP_BO_TILED_8:
|
||||
case OMAP_BO_TILED_16:
|
||||
case OMAP_BO_TILED_32:
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* GEM buffer object constructor */
|
||||
struct drm_gem_object *omap_gem_new(struct drm_device *dev,
|
||||
union omap_gem_size gsize, u32 flags)
|
||||
@ -1131,18 +1176,15 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
/* Validate the flags and compute the memory and cache flags. */
|
||||
if (flags & OMAP_BO_TILED) {
|
||||
if (!priv->usergart) {
|
||||
dev_err(dev->dev, "Tiled buffers require DMM\n");
|
||||
return NULL;
|
||||
}
|
||||
if (!omap_gem_validate_flags(dev, flags))
|
||||
return NULL;
|
||||
|
||||
/* Validate the flags and compute the memory and cache flags. */
|
||||
if (flags & OMAP_BO_TILED_MASK) {
|
||||
/*
|
||||
* Tiled buffers are always shmem paged backed. When they are
|
||||
* scanned out, they are remapped into DMM/TILER.
|
||||
*/
|
||||
flags &= ~OMAP_BO_SCANOUT;
|
||||
flags |= OMAP_BO_MEM_SHMEM;
|
||||
|
||||
/*
|
||||
@ -1153,9 +1195,8 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
|
||||
flags |= tiler_get_cpu_cache_flags();
|
||||
} else if ((flags & OMAP_BO_SCANOUT) && !priv->has_dmm) {
|
||||
/*
|
||||
* OMAP_BO_SCANOUT hints that the buffer doesn't need to be
|
||||
* tiled. However, to lower the pressure on memory allocation,
|
||||
* use contiguous memory only if no TILER is available.
|
||||
* If we don't have DMM, we must allocate scanout buffers
|
||||
* from contiguous DMA memory.
|
||||
*/
|
||||
flags |= OMAP_BO_MEM_DMA_API;
|
||||
} else if (!(flags & OMAP_BO_MEM_DMABUF)) {
|
||||
@ -1174,7 +1215,7 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
|
||||
omap_obj->flags = flags;
|
||||
mutex_init(&omap_obj->lock);
|
||||
|
||||
if (flags & OMAP_BO_TILED) {
|
||||
if (flags & OMAP_BO_TILED_MASK) {
|
||||
/*
|
||||
* For tiled buffers align dimensions to slot boundaries and
|
||||
* calculate size based on aligned dimensions.
|
||||
|
@ -67,7 +67,7 @@ static int omap_gem_dmabuf_begin_cpu_access(struct dma_buf *buffer,
|
||||
{
|
||||
struct drm_gem_object *obj = buffer->priv;
|
||||
struct page **pages;
|
||||
if (omap_gem_flags(obj) & OMAP_BO_TILED) {
|
||||
if (omap_gem_flags(obj) & OMAP_BO_TILED_MASK) {
|
||||
/* TODO we would need to pin at least part of the buffer to
|
||||
* get de-tiled view. For now just reject it.
|
||||
*/
|
||||
|
@ -10,3 +10,5 @@
|
||||
|
||||
- Compute job support. So called 'compute only' jobs need to be plumbed up to
|
||||
userspace.
|
||||
|
||||
- Support core dump on job failure
|
||||
|
@ -53,10 +53,8 @@ static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
|
||||
if (err) {
|
||||
dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate,
|
||||
err);
|
||||
if (pfdev->regulator)
|
||||
regulator_set_voltage(pfdev->regulator,
|
||||
pfdev->devfreq.cur_volt,
|
||||
pfdev->devfreq.cur_volt);
|
||||
regulator_set_voltage(pfdev->regulator, pfdev->devfreq.cur_volt,
|
||||
pfdev->devfreq.cur_volt);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -470,7 +470,7 @@ static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = {
|
||||
PANFROST_IOCTL(MADVISE, madvise, DRM_RENDER_ALLOW),
|
||||
};
|
||||
|
||||
DEFINE_DRM_GEM_SHMEM_FOPS(panfrost_drm_driver_fops);
|
||||
DEFINE_DRM_GEM_FOPS(panfrost_drm_driver_fops);
|
||||
|
||||
/*
|
||||
* Panfrost driver version:
|
||||
|
@ -112,7 +112,7 @@ static const struct drm_gem_object_funcs panfrost_gem_funcs = {
|
||||
.get_sg_table = drm_gem_shmem_get_sg_table,
|
||||
.vmap = drm_gem_shmem_vmap,
|
||||
.vunmap = drm_gem_shmem_vunmap,
|
||||
.vm_ops = &drm_gem_shmem_vm_ops,
|
||||
.mmap = drm_gem_shmem_mmap,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -404,8 +404,6 @@ static void panfrost_job_timedout(struct drm_sched_job *sched_job)
|
||||
}
|
||||
spin_unlock_irqrestore(&pfdev->js->job_lock, flags);
|
||||
|
||||
/* panfrost_core_dump(pfdev); */
|
||||
|
||||
panfrost_devfreq_record_transition(pfdev, js);
|
||||
panfrost_device_reset(pfdev);
|
||||
|
||||
|
@ -4,6 +4,7 @@ config DRM_QXL
|
||||
depends on DRM && PCI && MMU
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_TTM
|
||||
select DRM_TTM_HELPER
|
||||
select CRC32
|
||||
help
|
||||
QXL virtual GPU for Spice virtualization desktop integration.
|
||||
|
@ -150,15 +150,7 @@ qxl_pci_remove(struct pci_dev *pdev)
|
||||
drm_dev_put(dev);
|
||||
}
|
||||
|
||||
static const struct file_operations qxl_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.mmap = qxl_mmap,
|
||||
};
|
||||
DEFINE_DRM_GEM_FOPS(qxl_fops);
|
||||
|
||||
static int qxl_drm_freeze(struct drm_device *dev)
|
||||
{
|
||||
|
@ -355,7 +355,6 @@ int qxl_mode_dumb_mmap(struct drm_file *filp,
|
||||
/* qxl ttm */
|
||||
int qxl_ttm_init(struct qxl_device *qdev);
|
||||
void qxl_ttm_fini(struct qxl_device *qdev);
|
||||
int qxl_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
|
||||
/* qxl image */
|
||||
|
||||
|
@ -54,9 +54,14 @@ bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo)
|
||||
void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned)
|
||||
{
|
||||
u32 c = 0;
|
||||
u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0;
|
||||
u32 pflag = 0;
|
||||
unsigned int i;
|
||||
|
||||
if (pinned)
|
||||
pflag |= TTM_PL_FLAG_NO_EVICT;
|
||||
if (qbo->tbo.base.size <= PAGE_SIZE)
|
||||
pflag |= TTM_PL_FLAG_TOPDOWN;
|
||||
|
||||
qbo->placement.placement = qbo->placements;
|
||||
qbo->placement.busy_placement = qbo->placements;
|
||||
if (domain == QXL_GEM_DOMAIN_VRAM)
|
||||
@ -86,6 +91,7 @@ static const struct drm_gem_object_funcs qxl_object_funcs = {
|
||||
.get_sg_table = qxl_gem_prime_get_sg_table,
|
||||
.vmap = qxl_gem_prime_vmap,
|
||||
.vunmap = qxl_gem_prime_vunmap,
|
||||
.mmap = drm_gem_ttm_mmap,
|
||||
.print_info = drm_gem_ttm_print_info,
|
||||
};
|
||||
|
||||
|
@ -48,47 +48,6 @@ static struct qxl_device *qxl_get_qdev(struct ttm_bo_device *bdev)
|
||||
return qdev;
|
||||
}
|
||||
|
||||
static struct vm_operations_struct qxl_ttm_vm_ops;
|
||||
static const struct vm_operations_struct *ttm_vm_ops;
|
||||
|
||||
static vm_fault_t qxl_ttm_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct ttm_buffer_object *bo;
|
||||
vm_fault_t ret;
|
||||
|
||||
bo = (struct ttm_buffer_object *)vmf->vma->vm_private_data;
|
||||
if (bo == NULL)
|
||||
return VM_FAULT_NOPAGE;
|
||||
ret = ttm_vm_ops->fault(vmf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int qxl_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
int r;
|
||||
struct drm_file *file_priv = filp->private_data;
|
||||
struct qxl_device *qdev = file_priv->minor->dev->dev_private;
|
||||
|
||||
if (qdev == NULL) {
|
||||
DRM_ERROR(
|
||||
"filp->private_data->minor->dev->dev_private == NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
DRM_DEBUG_DRIVER("filp->private_data = 0x%p, vma->vm_pgoff = %lx\n",
|
||||
filp->private_data, vma->vm_pgoff);
|
||||
|
||||
r = ttm_bo_mmap(filp, vma, &qdev->mman.bdev);
|
||||
if (unlikely(r != 0))
|
||||
return r;
|
||||
if (unlikely(ttm_vm_ops == NULL)) {
|
||||
ttm_vm_ops = vma->vm_ops;
|
||||
qxl_ttm_vm_ops = *ttm_vm_ops;
|
||||
qxl_ttm_vm_ops.fault = &qxl_ttm_fault;
|
||||
}
|
||||
vma->vm_ops = &qxl_ttm_vm_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qxl_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
|
||||
{
|
||||
return 0;
|
||||
@ -151,14 +110,6 @@ static void qxl_evict_flags(struct ttm_buffer_object *bo,
|
||||
*placement = qbo->placement;
|
||||
}
|
||||
|
||||
static int qxl_verify_access(struct ttm_buffer_object *bo, struct file *filp)
|
||||
{
|
||||
struct qxl_bo *qbo = to_qxl_bo(bo);
|
||||
|
||||
return drm_vma_node_verify_access(&qbo->tbo.base.vma_node,
|
||||
filp->private_data);
|
||||
}
|
||||
|
||||
static int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
|
||||
struct ttm_mem_reg *mem)
|
||||
{
|
||||
@ -310,7 +261,6 @@ static struct ttm_bo_driver qxl_bo_driver = {
|
||||
.eviction_valuable = ttm_bo_eviction_valuable,
|
||||
.evict_flags = &qxl_evict_flags,
|
||||
.move = &qxl_bo_move,
|
||||
.verify_access = &qxl_verify_access,
|
||||
.io_mem_reserve = &qxl_ttm_io_mem_reserve,
|
||||
.io_mem_free = &qxl_ttm_io_mem_free,
|
||||
.move_notify = &qxl_bo_move_notify,
|
||||
|
@ -477,8 +477,8 @@ static int cdn_dp_disable(struct cdn_dp_device *dp)
|
||||
cdn_dp_set_firmware_active(dp, false);
|
||||
cdn_dp_clk_disable(dp);
|
||||
dp->active = false;
|
||||
dp->link.rate = 0;
|
||||
dp->link.num_lanes = 0;
|
||||
dp->max_lanes = 0;
|
||||
dp->max_rate = 0;
|
||||
if (!dp->connected) {
|
||||
kfree(dp->edid);
|
||||
dp->edid = NULL;
|
||||
@ -570,7 +570,7 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
|
||||
struct cdn_dp_port *port = cdn_dp_connected_port(dp);
|
||||
u8 sink_lanes = drm_dp_max_lane_count(dp->dpcd);
|
||||
|
||||
if (!port || !dp->link.rate || !dp->link.num_lanes)
|
||||
if (!port || !dp->max_rate || !dp->max_lanes)
|
||||
return false;
|
||||
|
||||
if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
|
||||
@ -952,8 +952,8 @@ static void cdn_dp_pd_event_work(struct work_struct *work)
|
||||
|
||||
/* Enabled and connected with a sink, re-train if requested */
|
||||
} else if (!cdn_dp_check_link_status(dp)) {
|
||||
unsigned int rate = dp->link.rate;
|
||||
unsigned int lanes = dp->link.num_lanes;
|
||||
unsigned int rate = dp->max_rate;
|
||||
unsigned int lanes = dp->max_lanes;
|
||||
struct drm_display_mode *mode = &dp->mode;
|
||||
|
||||
DRM_DEV_INFO(dp->dev, "Connected with sink. Re-train link\n");
|
||||
@ -966,7 +966,7 @@ static void cdn_dp_pd_event_work(struct work_struct *work)
|
||||
|
||||
/* If training result is changed, update the video config */
|
||||
if (mode->clock &&
|
||||
(rate != dp->link.rate || lanes != dp->link.num_lanes)) {
|
||||
(rate != dp->max_rate || lanes != dp->max_lanes)) {
|
||||
ret = cdn_dp_config_video(dp);
|
||||
if (ret) {
|
||||
dp->connected = false;
|
||||
|
@ -92,9 +92,10 @@ struct cdn_dp_device {
|
||||
struct reset_control *core_rst;
|
||||
struct audio_info audio_info;
|
||||
struct video_info video_info;
|
||||
struct drm_dp_link link;
|
||||
struct cdn_dp_port *port[MAX_PHY];
|
||||
u8 ports;
|
||||
u8 max_lanes;
|
||||
u8 max_rate;
|
||||
u8 lanes;
|
||||
int active_port;
|
||||
|
||||
|
@ -535,8 +535,8 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
|
||||
if (ret)
|
||||
goto err_get_training_status;
|
||||
|
||||
dp->link.rate = drm_dp_bw_code_to_link_rate(status[0]);
|
||||
dp->link.num_lanes = status[1];
|
||||
dp->max_rate = drm_dp_bw_code_to_link_rate(status[0]);
|
||||
dp->max_lanes = status[1];
|
||||
|
||||
err_get_training_status:
|
||||
if (ret)
|
||||
@ -560,8 +560,8 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate,
|
||||
dp->link.num_lanes);
|
||||
DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->max_rate,
|
||||
dp->max_lanes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -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 = dp->link.rate / 1000;
|
||||
link_rate = dp->max_rate / 1000;
|
||||
|
||||
ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE);
|
||||
if (ret)
|
||||
@ -659,14 +659,13 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
|
||||
do {
|
||||
tu_size_reg += 2;
|
||||
symbol = tu_size_reg * mode->clock * bit_per_pix;
|
||||
do_div(symbol, dp->link.num_lanes * link_rate * 8);
|
||||
do_div(symbol, dp->max_lanes * link_rate * 8);
|
||||
rem = do_div(symbol, 1000);
|
||||
if (tu_size_reg > 64) {
|
||||
ret = -EINVAL;
|
||||
DRM_DEV_ERROR(dp->dev,
|
||||
"tu error, clk:%d, lanes:%d, rate:%d\n",
|
||||
mode->clock, dp->link.num_lanes,
|
||||
link_rate);
|
||||
mode->clock, dp->max_lanes, link_rate);
|
||||
goto err_config_video;
|
||||
}
|
||||
} while ((symbol <= 1) || (tu_size_reg - symbol < 4) ||
|
||||
@ -680,7 +679,7 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
|
||||
|
||||
/* set the FIFO Buffer size */
|
||||
val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate;
|
||||
val /= (dp->link.num_lanes * link_rate);
|
||||
val /= (dp->max_lanes * link_rate);
|
||||
val = div_u64(8 * (symbol + 1), bit_per_pix) - val;
|
||||
val += 2;
|
||||
ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
|
||||
@ -833,7 +832,7 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
|
||||
u32 val;
|
||||
|
||||
if (audio->channels == 2) {
|
||||
if (dp->link.num_lanes == 1)
|
||||
if (dp->max_lanes == 1)
|
||||
sub_pckt_num = 2;
|
||||
else
|
||||
sub_pckt_num = 4;
|
||||
|
@ -450,6 +450,7 @@ static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
|
||||
.phy_ops = &rk3328_hdmi_phy_ops,
|
||||
.phy_name = "inno_dw_hdmi_phy2",
|
||||
.phy_force_vendor = true,
|
||||
.use_drm_infoframe = true,
|
||||
};
|
||||
|
||||
static struct rockchip_hdmi_chip_data rk3399_chip_data = {
|
||||
@ -464,6 +465,7 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
|
||||
.cur_ctr = rockchip_cur_ctr,
|
||||
.phy_config = rockchip_phy_config,
|
||||
.phy_data = &rk3399_chip_data,
|
||||
.use_drm_infoframe = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
|
||||
|
@ -743,7 +743,6 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *drm = data;
|
||||
struct rk3066_hdmi *hdmi;
|
||||
struct resource *iores;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
@ -753,12 +752,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
|
||||
|
||||
hdmi->dev = dev;
|
||||
hdmi->drm_dev = drm;
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!iores)
|
||||
return -ENXIO;
|
||||
|
||||
hdmi->regs = devm_ioremap_resource(dev, iores);
|
||||
hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(hdmi->regs))
|
||||
return PTR_ERR(hdmi->regs);
|
||||
|
||||
|
@ -294,7 +294,7 @@ static void rockchip_gem_release_object(struct rockchip_gem_object *rk_obj)
|
||||
kfree(rk_obj);
|
||||
}
|
||||
|
||||
struct rockchip_gem_object *
|
||||
static struct rockchip_gem_object *
|
||||
rockchip_gem_alloc_object(struct drm_device *drm, unsigned int size)
|
||||
{
|
||||
struct rockchip_gem_object *rk_obj;
|
||||
|
@ -139,6 +139,7 @@ struct vop {
|
||||
|
||||
uint32_t *regsbak;
|
||||
void __iomem *regs;
|
||||
void __iomem *lut_regs;
|
||||
|
||||
/* physical map length of vop register */
|
||||
uint32_t len;
|
||||
@ -1040,14 +1041,118 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct vop *vop = to_vop(crtc);
|
||||
unsigned long rate;
|
||||
|
||||
adjusted_mode->clock =
|
||||
DIV_ROUND_UP(clk_round_rate(vop->dclk,
|
||||
adjusted_mode->clock * 1000), 1000);
|
||||
/*
|
||||
* Clock craziness.
|
||||
*
|
||||
* Key points:
|
||||
*
|
||||
* - DRM works in in kHz.
|
||||
* - Clock framework works in Hz.
|
||||
* - Rockchip's clock driver picks the clock rate that is the
|
||||
* same _OR LOWER_ than the one requested.
|
||||
*
|
||||
* Action plan:
|
||||
*
|
||||
* 1. When DRM gives us a mode, we should add 999 Hz to it. That way
|
||||
* if the clock we need is 60000001 Hz (~60 MHz) and DRM tells us to
|
||||
* make 60000 kHz then the clock framework will actually give us
|
||||
* the right clock.
|
||||
*
|
||||
* NOTE: if the PLL (maybe through a divider) could actually make
|
||||
* a clock rate 999 Hz higher instead of the one we want then this
|
||||
* could be a problem. Unfortunately there's not much we can do
|
||||
* since it's baked into DRM to use kHz. It shouldn't matter in
|
||||
* practice since Rockchip PLLs are controlled by tables and
|
||||
* even if there is a divider in the middle I wouldn't expect PLL
|
||||
* rates in the table that are just a few kHz different.
|
||||
*
|
||||
* 2. Get the clock framework to round the rate for us to tell us
|
||||
* what it will actually make.
|
||||
*
|
||||
* 3. Store the rounded up rate so that we don't need to worry about
|
||||
* this in the actual clk_set_rate().
|
||||
*/
|
||||
rate = clk_round_rate(vop->dclk, adjusted_mode->clock * 1000 + 999);
|
||||
adjusted_mode->clock = DIV_ROUND_UP(rate, 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vop_dsp_lut_is_enabled(struct vop *vop)
|
||||
{
|
||||
return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en);
|
||||
}
|
||||
|
||||
static void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_color_lut *lut = crtc->state->gamma_lut->data;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < crtc->gamma_size; i++) {
|
||||
u32 word;
|
||||
|
||||
word = (drm_color_lut_extract(lut[i].red, 10) << 20) |
|
||||
(drm_color_lut_extract(lut[i].green, 10) << 10) |
|
||||
drm_color_lut_extract(lut[i].blue, 10);
|
||||
writel(word, vop->lut_regs + i * 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
struct drm_crtc_state *state = crtc->state;
|
||||
unsigned int idle;
|
||||
int ret;
|
||||
|
||||
if (!vop->lut_regs)
|
||||
return;
|
||||
/*
|
||||
* To disable gamma (gamma_lut is null) or to write
|
||||
* an update to the LUT, clear dsp_lut_en.
|
||||
*/
|
||||
spin_lock(&vop->reg_lock);
|
||||
VOP_REG_SET(vop, common, dsp_lut_en, 0);
|
||||
vop_cfg_done(vop);
|
||||
spin_unlock(&vop->reg_lock);
|
||||
|
||||
/*
|
||||
* In order to write the LUT to the internal memory,
|
||||
* we need to first make sure the dsp_lut_en bit is cleared.
|
||||
*/
|
||||
ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop,
|
||||
idle, !idle, 5, 30 * 1000);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state->gamma_lut)
|
||||
return;
|
||||
|
||||
spin_lock(&vop->reg_lock);
|
||||
vop_crtc_write_gamma_lut(vop, crtc);
|
||||
VOP_REG_SET(vop, common, dsp_lut_en, 1);
|
||||
vop_cfg_done(vop);
|
||||
spin_unlock(&vop->reg_lock);
|
||||
}
|
||||
|
||||
static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
struct vop *vop = to_vop(crtc);
|
||||
|
||||
/*
|
||||
* Only update GAMMA if the 'active' flag is not changed,
|
||||
* otherwise it's updated by .atomic_enable.
|
||||
*/
|
||||
if (crtc->state->color_mgmt_changed &&
|
||||
!crtc->state->active_changed)
|
||||
vop_crtc_gamma_set(vop, crtc, old_crtc_state);
|
||||
}
|
||||
|
||||
static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
@ -1075,6 +1180,14 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a GAMMA LUT in the state, then let's make sure
|
||||
* it's updated. We might be coming out of suspend,
|
||||
* which means the LUT internal memory needs to be re-written.
|
||||
*/
|
||||
if (crtc->state->gamma_lut)
|
||||
vop_crtc_gamma_set(vop, crtc, old_state);
|
||||
|
||||
mutex_lock(&vop->vop_lock);
|
||||
|
||||
WARN_ON(vop->event);
|
||||
@ -1085,9 +1198,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
pin_pol = BIT(DCLK_INVERT);
|
||||
pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) ?
|
||||
pin_pol = (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) ?
|
||||
BIT(HSYNC_POSITIVE) : 0;
|
||||
pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ?
|
||||
BIT(VSYNC_POSITIVE) : 0;
|
||||
@ -1096,25 +1207,29 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
|
||||
switch (s->output_type) {
|
||||
case DRM_MODE_CONNECTOR_LVDS:
|
||||
VOP_REG_SET(vop, output, rgb_en, 1);
|
||||
VOP_REG_SET(vop, output, rgb_dclk_pol, 1);
|
||||
VOP_REG_SET(vop, output, rgb_pin_pol, pin_pol);
|
||||
VOP_REG_SET(vop, output, rgb_en, 1);
|
||||
break;
|
||||
case DRM_MODE_CONNECTOR_eDP:
|
||||
VOP_REG_SET(vop, output, edp_dclk_pol, 1);
|
||||
VOP_REG_SET(vop, output, edp_pin_pol, pin_pol);
|
||||
VOP_REG_SET(vop, output, edp_en, 1);
|
||||
break;
|
||||
case DRM_MODE_CONNECTOR_HDMIA:
|
||||
VOP_REG_SET(vop, output, hdmi_dclk_pol, 1);
|
||||
VOP_REG_SET(vop, output, hdmi_pin_pol, pin_pol);
|
||||
VOP_REG_SET(vop, output, hdmi_en, 1);
|
||||
break;
|
||||
case DRM_MODE_CONNECTOR_DSI:
|
||||
VOP_REG_SET(vop, output, mipi_dclk_pol, 1);
|
||||
VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol);
|
||||
VOP_REG_SET(vop, output, mipi_en, 1);
|
||||
VOP_REG_SET(vop, output, mipi_dual_channel_en,
|
||||
!!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL));
|
||||
break;
|
||||
case DRM_MODE_CONNECTOR_DisplayPort:
|
||||
pin_pol &= ~BIT(DCLK_INVERT);
|
||||
VOP_REG_SET(vop, output, dp_dclk_pol, 0);
|
||||
VOP_REG_SET(vop, output, dp_pin_pol, pin_pol);
|
||||
VOP_REG_SET(vop, output, dp_en, 1);
|
||||
break;
|
||||
@ -1191,6 +1306,26 @@ static void vop_wait_for_irq_handler(struct vop *vop)
|
||||
synchronize_irq(vop->irq);
|
||||
}
|
||||
|
||||
static int vop_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *crtc_state)
|
||||
{
|
||||
struct vop *vop = to_vop(crtc);
|
||||
|
||||
if (vop->lut_regs && crtc_state->color_mgmt_changed &&
|
||||
crtc_state->gamma_lut) {
|
||||
unsigned int len;
|
||||
|
||||
len = drm_color_lut_size(crtc_state->gamma_lut);
|
||||
if (len != crtc->gamma_size) {
|
||||
DRM_DEBUG_KMS("Invalid LUT size; got %d, expected %d\n",
|
||||
len, crtc->gamma_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
@ -1243,6 +1378,8 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
|
||||
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
|
||||
.mode_fixup = vop_crtc_mode_fixup,
|
||||
.atomic_check = vop_crtc_atomic_check,
|
||||
.atomic_begin = vop_crtc_atomic_begin,
|
||||
.atomic_flush = vop_crtc_atomic_flush,
|
||||
.atomic_enable = vop_crtc_atomic_enable,
|
||||
.atomic_disable = vop_crtc_atomic_disable,
|
||||
@ -1361,6 +1498,7 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
|
||||
.disable_vblank = vop_crtc_disable_vblank,
|
||||
.set_crc_source = vop_crtc_set_crc_source,
|
||||
.verify_crc_source = vop_crtc_verify_crc_source,
|
||||
.gamma_set = drm_atomic_helper_legacy_gamma_set,
|
||||
};
|
||||
|
||||
static void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
|
||||
@ -1518,6 +1656,10 @@ static int vop_create_crtc(struct vop *vop)
|
||||
goto err_cleanup_planes;
|
||||
|
||||
drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs);
|
||||
if (vop->lut_regs) {
|
||||
drm_mode_crtc_set_gamma_size(crtc, vop_data->lut_size);
|
||||
drm_crtc_enable_color_mgmt(crtc, 0, false, vop_data->lut_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create drm_planes for overlay windows with possible_crtcs restricted
|
||||
@ -1822,6 +1964,17 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
|
||||
if (IS_ERR(vop->regs))
|
||||
return PTR_ERR(vop->regs);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (res) {
|
||||
if (!vop_data->lut_size) {
|
||||
DRM_DEV_ERROR(dev, "no gamma LUT size defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
vop->lut_regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(vop->lut_regs))
|
||||
return PTR_ERR(vop->lut_regs);
|
||||
}
|
||||
|
||||
vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL);
|
||||
if (!vop->regsbak)
|
||||
return -ENOMEM;
|
||||
|
@ -46,10 +46,15 @@ struct vop_modeset {
|
||||
struct vop_output {
|
||||
struct vop_reg pin_pol;
|
||||
struct vop_reg dp_pin_pol;
|
||||
struct vop_reg dp_dclk_pol;
|
||||
struct vop_reg edp_pin_pol;
|
||||
struct vop_reg edp_dclk_pol;
|
||||
struct vop_reg hdmi_pin_pol;
|
||||
struct vop_reg hdmi_dclk_pol;
|
||||
struct vop_reg mipi_pin_pol;
|
||||
struct vop_reg mipi_dclk_pol;
|
||||
struct vop_reg rgb_pin_pol;
|
||||
struct vop_reg rgb_dclk_pol;
|
||||
struct vop_reg dp_en;
|
||||
struct vop_reg edp_en;
|
||||
struct vop_reg hdmi_en;
|
||||
@ -67,6 +72,7 @@ struct vop_common {
|
||||
struct vop_reg dither_down_mode;
|
||||
struct vop_reg dither_down_en;
|
||||
struct vop_reg dither_up;
|
||||
struct vop_reg dsp_lut_en;
|
||||
struct vop_reg gate_en;
|
||||
struct vop_reg mmu_en;
|
||||
struct vop_reg out_mode;
|
||||
@ -170,6 +176,7 @@ struct vop_data {
|
||||
const struct vop_win_yuv2yuv_data *win_yuv2yuv;
|
||||
const struct vop_win_data *win;
|
||||
unsigned int win_size;
|
||||
unsigned int lut_size;
|
||||
|
||||
#define VOP_FEATURE_OUTPUT_RGB10 BIT(0)
|
||||
#define VOP_FEATURE_INTERNAL_RGB BIT(1)
|
||||
@ -294,8 +301,7 @@ enum dither_down_mode_sel {
|
||||
enum vop_pol {
|
||||
HSYNC_POSITIVE = 0,
|
||||
VSYNC_POSITIVE = 1,
|
||||
DEN_NEGATIVE = 2,
|
||||
DCLK_INVERT = 3
|
||||
DEN_NEGATIVE = 2
|
||||
};
|
||||
|
||||
#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "rockchip_drm_vop.h"
|
||||
#include "rockchip_vop_reg.h"
|
||||
#include "rockchip_drm_drv.h"
|
||||
|
||||
#define _VOP_REG(off, _mask, _shift, _write_mask, _relaxed) \
|
||||
{ \
|
||||
@ -214,9 +215,11 @@ static const struct vop_modeset px30_modeset = {
|
||||
};
|
||||
|
||||
static const struct vop_output px30_output = {
|
||||
.rgb_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0xf, 1),
|
||||
.mipi_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0xf, 25),
|
||||
.rgb_dclk_pol = VOP_REG(PX30_DSP_CTRL0, 0x1, 1),
|
||||
.rgb_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0x7, 2),
|
||||
.rgb_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 0),
|
||||
.mipi_dclk_pol = VOP_REG(PX30_DSP_CTRL0, 0x1, 25),
|
||||
.mipi_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0x7, 26),
|
||||
.mipi_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 24),
|
||||
};
|
||||
|
||||
@ -598,6 +601,7 @@ static const struct vop_common rk3288_common = {
|
||||
.dither_down_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 2),
|
||||
.pre_dither_down = VOP_REG(RK3288_DSP_CTRL1, 0x1, 1),
|
||||
.dither_up = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6),
|
||||
.dsp_lut_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 0),
|
||||
.data_blank = VOP_REG(RK3288_DSP_CTRL0, 0x1, 19),
|
||||
.dsp_blank = VOP_REG(RK3288_DSP_CTRL0, 0x3, 18),
|
||||
.out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0),
|
||||
@ -646,6 +650,7 @@ static const struct vop_data rk3288_vop = {
|
||||
.output = &rk3288_output,
|
||||
.win = rk3288_vop_win_data,
|
||||
.win_size = ARRAY_SIZE(rk3288_vop_win_data),
|
||||
.lut_size = 1024,
|
||||
};
|
||||
|
||||
static const int rk3368_vop_intrs[] = {
|
||||
@ -717,10 +722,14 @@ static const struct vop_win_data rk3368_vop_win_data[] = {
|
||||
};
|
||||
|
||||
static const struct vop_output rk3368_output = {
|
||||
.rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 16),
|
||||
.hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 20),
|
||||
.edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 24),
|
||||
.mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 28),
|
||||
.rgb_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 19),
|
||||
.hdmi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 23),
|
||||
.edp_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 27),
|
||||
.mipi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 31),
|
||||
.rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 16),
|
||||
.hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 20),
|
||||
.edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 24),
|
||||
.mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 28),
|
||||
.rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12),
|
||||
.hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13),
|
||||
.edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14),
|
||||
@ -764,11 +773,16 @@ static const struct vop_data rk3366_vop = {
|
||||
};
|
||||
|
||||
static const struct vop_output rk3399_output = {
|
||||
.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
|
||||
.rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 16),
|
||||
.hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 20),
|
||||
.edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 24),
|
||||
.mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 28),
|
||||
.dp_dclk_pol = VOP_REG(RK3399_DSP_CTRL1, 0x1, 19),
|
||||
.rgb_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 19),
|
||||
.hdmi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 23),
|
||||
.edp_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 27),
|
||||
.mipi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 31),
|
||||
.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0x7, 16),
|
||||
.rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 16),
|
||||
.hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 20),
|
||||
.edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 24),
|
||||
.mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 28),
|
||||
.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
|
||||
.rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12),
|
||||
.hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13),
|
||||
@ -872,14 +886,18 @@ static const struct vop_modeset rk3328_modeset = {
|
||||
};
|
||||
|
||||
static const struct vop_output rk3328_output = {
|
||||
.rgb_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 19),
|
||||
.hdmi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 23),
|
||||
.edp_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 27),
|
||||
.mipi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 31),
|
||||
.rgb_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 12),
|
||||
.hdmi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 13),
|
||||
.edp_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 14),
|
||||
.mipi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 15),
|
||||
.rgb_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0xf, 16),
|
||||
.hdmi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0xf, 20),
|
||||
.edp_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0xf, 24),
|
||||
.mipi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0xf, 28),
|
||||
.rgb_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 16),
|
||||
.hdmi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 20),
|
||||
.edp_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 24),
|
||||
.mipi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 28),
|
||||
};
|
||||
|
||||
static const struct vop_misc rk3328_misc = {
|
||||
|
@ -128,13 +128,13 @@ static void drm_sched_fence_release_finished(struct dma_fence *f)
|
||||
dma_fence_put(&fence->scheduled);
|
||||
}
|
||||
|
||||
const struct dma_fence_ops drm_sched_fence_ops_scheduled = {
|
||||
static const struct dma_fence_ops drm_sched_fence_ops_scheduled = {
|
||||
.get_driver_name = drm_sched_fence_get_driver_name,
|
||||
.get_timeline_name = drm_sched_fence_get_timeline_name,
|
||||
.release = drm_sched_fence_release_scheduled,
|
||||
};
|
||||
|
||||
const struct dma_fence_ops drm_sched_fence_ops_finished = {
|
||||
static const struct dma_fence_ops drm_sched_fence_ops_finished = {
|
||||
.get_driver_name = drm_sched_fence_get_driver_name,
|
||||
.get_timeline_name = drm_sched_fence_get_timeline_name,
|
||||
.release = drm_sched_fence_release_finished,
|
||||
|
@ -226,6 +226,7 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
|
||||
sun8i_hdmi_phy_init(hdmi->phy);
|
||||
|
||||
plat_data->mode_valid = hdmi->quirks->mode_valid;
|
||||
plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe;
|
||||
sun8i_hdmi_phy_set_ops(hdmi->phy, plat_data);
|
||||
|
||||
platform_set_drvdata(pdev, hdmi);
|
||||
@ -300,6 +301,7 @@ static const struct sun8i_dw_hdmi_quirks sun8i_a83t_quirks = {
|
||||
|
||||
static const struct sun8i_dw_hdmi_quirks sun50i_h6_quirks = {
|
||||
.mode_valid = sun8i_dw_hdmi_mode_valid_h6,
|
||||
.use_drm_infoframe = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id sun8i_dw_hdmi_dt_ids[] = {
|
||||
|
@ -179,6 +179,7 @@ struct sun8i_dw_hdmi_quirks {
|
||||
enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
|
||||
const struct drm_display_mode *mode);
|
||||
unsigned int set_rate : 1;
|
||||
unsigned int use_drm_infoframe : 1;
|
||||
};
|
||||
|
||||
struct sun8i_dw_hdmi {
|
||||
|
@ -5,6 +5,7 @@ tegra-drm-y := \
|
||||
drm.o \
|
||||
gem.o \
|
||||
fb.o \
|
||||
dp.o \
|
||||
hub.o \
|
||||
plane.o \
|
||||
dc.o \
|
||||
|
133
drivers/gpu/drm/tegra/dp.c
Normal file
133
drivers/gpu/drm/tegra/dp.c
Normal file
@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright (C) 2013-2019 NVIDIA Corporation
|
||||
* Copyright (C) 2015 Rob Clark
|
||||
*/
|
||||
|
||||
#include <drm/drm_dp_helper.h>
|
||||
|
||||
#include "dp.h"
|
||||
|
||||
/**
|
||||
* drm_dp_link_probe() - probe a DisplayPort link for capabilities
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @link: pointer to structure in which to return link capabilities
|
||||
*
|
||||
* The structure filled in by this function can usually be passed directly
|
||||
* into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
|
||||
* configure the link based on the link's capabilities.
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
|
||||
{
|
||||
u8 values[3];
|
||||
int err;
|
||||
|
||||
memset(link, 0, sizeof(*link));
|
||||
|
||||
err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
link->revision = values[0];
|
||||
link->rate = drm_dp_bw_code_to_link_rate(values[1]);
|
||||
link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
|
||||
|
||||
if (values[2] & DP_ENHANCED_FRAME_CAP)
|
||||
link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_dp_link_power_up() - power up a DisplayPort link
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @link: pointer to a structure containing the link configuration
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
|
||||
{
|
||||
u8 value;
|
||||
int err;
|
||||
|
||||
/* DP_SET_POWER register is only available on DPCD v1.1 and later */
|
||||
if (link->revision < 0x11)
|
||||
return 0;
|
||||
|
||||
err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
value &= ~DP_SET_POWER_MASK;
|
||||
value |= DP_SET_POWER_D0;
|
||||
|
||||
err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* According to the DP 1.1 specification, a "Sink Device must exit the
|
||||
* power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
|
||||
* Control Field" (register 0x600).
|
||||
*/
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_dp_link_power_down() - power down a DisplayPort link
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @link: pointer to a structure containing the link configuration
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link)
|
||||
{
|
||||
u8 value;
|
||||
int err;
|
||||
|
||||
/* DP_SET_POWER register is only available on DPCD v1.1 and later */
|
||||
if (link->revision < 0x11)
|
||||
return 0;
|
||||
|
||||
err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
value &= ~DP_SET_POWER_MASK;
|
||||
value |= DP_SET_POWER_D3;
|
||||
|
||||
err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_dp_link_configure() - configure a DisplayPort link
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @link: pointer to a structure containing the link configuration
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
|
||||
{
|
||||
u8 values[2];
|
||||
int err;
|
||||
|
||||
values[0] = drm_dp_link_rate_to_bw_code(link->rate);
|
||||
values[1] = link->num_lanes;
|
||||
|
||||
if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
|
||||
values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
|
||||
|
||||
err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user