summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c36
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c108
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cik_ih.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cz_dpm.c273
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cz_dpm.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cz_ih.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/iceland_ih.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/tonga_ih.c7
-rw-r--r--drivers/gpu/drm/amd/include/cgs_common.h34
-rw-r--r--drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c6
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem.c6
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gpu.c7
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_debugfs.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c2
-rw-r--r--drivers/gpu/drm/rockchip/Kconfig10
-rw-r--r--drivers/gpu/drm/rockchip/Makefile1
-rw-r--r--drivers/gpu/drm/rockchip/dw-mipi-dsi.c1194
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c8
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c31
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c6
28 files changed, 1785 insertions, 30 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 003959f99251..313b0cc8d676 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -987,6 +987,8 @@ void amdgpu_vm_get_pd_bo(struct amdgpu_vm *vm,
struct list_head *validated,
struct amdgpu_bo_list_entry *entry);
void amdgpu_vm_get_pt_bos(struct amdgpu_vm *vm, struct list_head *duplicates);
+void amdgpu_vm_move_pt_bos_in_lru(struct amdgpu_device *adev,
+ struct amdgpu_vm *vm);
int amdgpu_vm_grab_id(struct amdgpu_vm *vm, struct amdgpu_ring *ring,
struct amdgpu_sync *sync);
void amdgpu_vm_flush(struct amdgpu_ring *ring,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c
index 59485d0b3cfb..a081dda9fa2f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c
@@ -398,6 +398,41 @@ static void amdgpu_cgs_write_pci_config_dword(void *cgs_device, unsigned addr,
WARN(ret, "pci_write_config_dword error");
}
+
+static int amdgpu_cgs_get_pci_resource(void *cgs_device,
+ enum cgs_resource_type resource_type,
+ uint64_t size,
+ uint64_t offset,
+ uint64_t *resource_base)
+{
+ CGS_FUNC_ADEV;
+
+ if (resource_base == NULL)
+ return -EINVAL;
+
+ switch (resource_type) {
+ case CGS_RESOURCE_TYPE_MMIO:
+ if (adev->rmmio_size == 0)
+ return -ENOENT;
+ if ((offset + size) > adev->rmmio_size)
+ return -EINVAL;
+ *resource_base = adev->rmmio_base;
+ return 0;
+ case CGS_RESOURCE_TYPE_DOORBELL:
+ if (adev->doorbell.size == 0)
+ return -ENOENT;
+ if ((offset + size) > adev->doorbell.size)
+ return -EINVAL;
+ *resource_base = adev->doorbell.base;
+ return 0;
+ case CGS_RESOURCE_TYPE_FB:
+ case CGS_RESOURCE_TYPE_IO:
+ case CGS_RESOURCE_TYPE_ROM:
+ default:
+ return -EINVAL;
+ }
+}
+
static const void *amdgpu_cgs_atom_get_data_table(void *cgs_device,
unsigned table, uint16_t *size,
uint8_t *frev, uint8_t *crev)
@@ -1041,6 +1076,7 @@ static const struct cgs_ops amdgpu_cgs_ops = {
amdgpu_cgs_write_pci_config_byte,
amdgpu_cgs_write_pci_config_word,
amdgpu_cgs_write_pci_config_dword,
+ amdgpu_cgs_get_pci_resource,
amdgpu_cgs_atom_get_data_table,
amdgpu_cgs_atom_get_cmd_table_revs,
amdgpu_cgs_atom_exec_cmd_table,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index ce0254d4dcd7..6f89f8e034d0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -421,15 +421,17 @@ static int amdgpu_cs_parser_relocs(struct amdgpu_cs_parser *p)
amdgpu_vm_get_pt_bos(&fpriv->vm, &duplicates);
- r = amdgpu_cs_list_validate(p->adev, &fpriv->vm, &p->validated);
+ r = amdgpu_cs_list_validate(p->adev, &fpriv->vm, &duplicates);
if (r)
goto error_validate;
- r = amdgpu_cs_list_validate(p->adev, &fpriv->vm, &duplicates);
+ r = amdgpu_cs_list_validate(p->adev, &fpriv->vm, &p->validated);
error_validate:
- if (r)
+ if (r) {
+ amdgpu_vm_move_pt_bos_in_lru(p->adev, &fpriv->vm);
ttm_eu_backoff_reservation(&p->ticket, &p->validated);
+ }
error_reserve:
if (need_mmap_lock)
@@ -473,8 +475,11 @@ static int cmp_size_smaller_first(void *priv, struct list_head *a,
**/
static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error, bool backoff)
{
+ struct amdgpu_fpriv *fpriv = parser->filp->driver_priv;
unsigned i;
+ amdgpu_vm_move_pt_bos_in_lru(parser->adev, &fpriv->vm);
+
if (!error) {
/* Sort the buffer list from the smallest to largest buffer,
* which affects the order of buffers in the LRU list.
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
index 7c42ff670080..3006182c5d0d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
@@ -312,6 +312,7 @@ int amdgpu_irq_add_id(struct amdgpu_device *adev, unsigned src_id,
}
adev->irq.sources[src_id] = source;
+
return 0;
}
@@ -335,15 +336,19 @@ void amdgpu_irq_dispatch(struct amdgpu_device *adev,
return;
}
- src = adev->irq.sources[src_id];
- if (!src) {
- DRM_DEBUG("Unhandled interrupt src_id: %d\n", src_id);
- return;
- }
+ if (adev->irq.virq[src_id]) {
+ generic_handle_irq(irq_find_mapping(adev->irq.domain, src_id));
+ } else {
+ src = adev->irq.sources[src_id];
+ if (!src) {
+ DRM_DEBUG("Unhandled interrupt src_id: %d\n", src_id);
+ return;
+ }
- r = src->funcs->process(adev, src, entry);
- if (r)
- DRM_ERROR("error processing interrupt (%d)\n", r);
+ r = src->funcs->process(adev, src, entry);
+ if (r)
+ DRM_ERROR("error processing interrupt (%d)\n", r);
+ }
}
/**
@@ -461,3 +466,90 @@ bool amdgpu_irq_enabled(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
return !!atomic_read(&src->enabled_types[type]);
}
+
+/* gen irq */
+static void amdgpu_irq_mask(struct irq_data *irqd)
+{
+ /* XXX */
+}
+
+static void amdgpu_irq_unmask(struct irq_data *irqd)
+{
+ /* XXX */
+}
+
+static struct irq_chip amdgpu_irq_chip = {
+ .name = "amdgpu-ih",
+ .irq_mask = amdgpu_irq_mask,
+ .irq_unmask = amdgpu_irq_unmask,
+};
+
+static int amdgpu_irqdomain_map(struct irq_domain *d,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ if (hwirq >= AMDGPU_MAX_IRQ_SRC_ID)
+ return -EPERM;
+
+ irq_set_chip_and_handler(irq,
+ &amdgpu_irq_chip, handle_simple_irq);
+ return 0;
+}
+
+static struct irq_domain_ops amdgpu_hw_irqdomain_ops = {
+ .map = amdgpu_irqdomain_map,
+};
+
+/**
+ * amdgpu_irq_add_domain - create a linear irq domain
+ *
+ * @adev: amdgpu device pointer
+ *
+ * Create an irq domain for GPU interrupt sources
+ * that may be driven by another driver (e.g., ACP).
+ */
+int amdgpu_irq_add_domain(struct amdgpu_device *adev)
+{
+ adev->irq.domain = irq_domain_add_linear(NULL, AMDGPU_MAX_IRQ_SRC_ID,
+ &amdgpu_hw_irqdomain_ops, adev);
+ if (!adev->irq.domain) {
+ DRM_ERROR("GPU irq add domain failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * amdgpu_irq_remove_domain - remove the irq domain
+ *
+ * @adev: amdgpu device pointer
+ *
+ * Remove the irq domain for GPU interrupt sources
+ * that may be driven by another driver (e.g., ACP).
+ */
+void amdgpu_irq_remove_domain(struct amdgpu_device *adev)
+{
+ if (adev->irq.domain) {
+ irq_domain_remove(adev->irq.domain);
+ adev->irq.domain = NULL;
+ }
+}
+
+/**
+ * amdgpu_irq_create_mapping - create a mapping between a domain irq and a
+ * Linux irq
+ *
+ * @adev: amdgpu device pointer
+ * @src_id: IH source id
+ *
+ * Create a mapping between a domain irq (GPU IH src id) and a Linux irq
+ * Use this for components that generate a GPU interrupt, but are driven
+ * by a different driver (e.g., ACP).
+ * Returns the Linux irq.
+ */
+unsigned amdgpu_irq_create_mapping(struct amdgpu_device *adev, unsigned src_id)
+{
+ adev->irq.virq[src_id] = irq_create_mapping(adev->irq.domain, src_id);
+
+ return adev->irq.virq[src_id];
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h
index 17b01aef4278..e124b59f39c1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h
@@ -24,6 +24,7 @@
#ifndef __AMDGPU_IRQ_H__
#define __AMDGPU_IRQ_H__
+#include <linux/irqdomain.h>
#include "amdgpu_ih.h"
#define AMDGPU_MAX_IRQ_SRC_ID 0x100
@@ -65,6 +66,10 @@ struct amdgpu_irq {
/* interrupt ring */
struct amdgpu_ih_ring ih;
const struct amdgpu_ih_funcs *ih_funcs;
+
+ /* gen irq stuff */
+ struct irq_domain *domain; /* GPU irq controller domain */
+ unsigned virq[AMDGPU_MAX_IRQ_SRC_ID];
};
void amdgpu_irq_preinstall(struct drm_device *dev);
@@ -90,4 +95,8 @@ int amdgpu_irq_put(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
bool amdgpu_irq_enabled(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
unsigned type);
+int amdgpu_irq_add_domain(struct amdgpu_device *adev);
+void amdgpu_irq_remove_domain(struct amdgpu_device *adev);
+unsigned amdgpu_irq_create_mapping(struct amdgpu_device *adev, unsigned src_id);
+
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
index 4386cbac7f97..7d8d84eaea4a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
@@ -326,7 +326,7 @@ static struct attribute *hwmon_attributes[] = {
static umode_t hwmon_attributes_visible(struct kobject *kobj,
struct attribute *attr, int index)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct amdgpu_device *adev = dev_get_drvdata(dev);
umode_t effective_mode = attr->mode;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
index dd005c336c97..181ce39ef5e5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
@@ -293,7 +293,8 @@ int amdgpu_sync_rings(struct amdgpu_sync *sync,
fence = to_amdgpu_fence(sync->sync_to[i]);
/* check if we really need to sync */
- if (!amdgpu_fence_need_sync(fence, ring))
+ if (!amdgpu_enable_scheduler &&
+ !amdgpu_fence_need_sync(fence, ring))
continue;
/* prevent GPU deadlocks */
@@ -303,7 +304,7 @@ int amdgpu_sync_rings(struct amdgpu_sync *sync,
}
if (amdgpu_enable_scheduler || !amdgpu_enable_semaphores) {
- r = fence_wait(&fence->base, true);
+ r = fence_wait(sync->sync_to[i], true);
if (r)
return r;
continue;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 8f7688e598a1..aefc668e6b5d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -119,6 +119,33 @@ void amdgpu_vm_get_pt_bos(struct amdgpu_vm *vm, struct list_head *duplicates)
list_add(&entry->tv.head, duplicates);
}
+
+}
+
+/**
+ * amdgpu_vm_move_pt_bos_in_lru - move the PT BOs to the LRU tail
+ *
+ * @adev: amdgpu device instance
+ * @vm: vm providing the BOs
+ *
+ * Move the PT BOs to the tail of the LRU.
+ */
+void amdgpu_vm_move_pt_bos_in_lru(struct amdgpu_device *adev,
+ struct amdgpu_vm *vm)
+{
+ struct ttm_bo_global *glob = adev->mman.bdev.glob;
+ unsigned i;
+
+ spin_lock(&glob->lru_lock);
+ for (i = 0; i <= vm->max_pde_used; ++i) {
+ struct amdgpu_bo_list_entry *entry = &vm->page_tables[i].entry;
+
+ if (!entry->robj)
+ continue;
+
+ ttm_bo_move_to_lru_tail(&entry->robj->tbo);
+ }
+ spin_unlock(&glob->lru_lock);
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/cik_ih.c b/drivers/gpu/drm/amd/amdgpu/cik_ih.c
index 8993c50cb89f..30c9b3beeef9 100644
--- a/drivers/gpu/drm/amd/amdgpu/cik_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/cik_ih.c
@@ -274,6 +274,11 @@ static void cik_ih_set_rptr(struct amdgpu_device *adev)
static int cik_ih_early_init(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ int ret;
+
+ ret = amdgpu_irq_add_domain(adev);
+ if (ret)
+ return ret;
cik_ih_set_interrupt_funcs(adev);
@@ -300,6 +305,7 @@ static int cik_ih_sw_fini(void *handle)
amdgpu_irq_fini(adev);
amdgpu_ih_ring_fini(adev);
+ amdgpu_irq_remove_domain(adev);
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c
index 8035d4d6a4f5..4dd17f2dd905 100644
--- a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c
+++ b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c
@@ -1078,6 +1078,37 @@ static uint32_t cz_get_eclk_level(struct amdgpu_device *adev,
return i;
}
+static uint32_t cz_get_uvd_level(struct amdgpu_device *adev,
+ uint32_t clock, uint16_t msg)
+{
+ int i = 0;
+ struct amdgpu_uvd_clock_voltage_dependency_table *table =
+ &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table;
+
+ switch (msg) {
+ case PPSMC_MSG_SetUvdSoftMin:
+ case PPSMC_MSG_SetUvdHardMin:
+ for (i = 0; i < table->count; i++)
+ if (clock <= table->entries[i].vclk)
+ break;
+ if (i == table->count)
+ i = table->count - 1;
+ break;
+ case PPSMC_MSG_SetUvdSoftMax:
+ case PPSMC_MSG_SetUvdHardMax:
+ for (i = table->count - 1; i >= 0; i--)
+ if (clock >= table->entries[i].vclk)
+ break;
+ if (i < 0)
+ i = 0;
+ break;
+ default:
+ break;
+ }
+
+ return i;
+}
+
static int cz_program_bootup_state(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
@@ -1739,6 +1770,200 @@ static int cz_dpm_unforce_dpm_levels(struct amdgpu_device *adev)
return 0;
}
+static int cz_dpm_uvd_force_highest(struct amdgpu_device *adev)
+{
+ struct cz_power_info *pi = cz_get_pi(adev);
+ int ret = 0;
+
+ if (pi->uvd_dpm.soft_min_clk != pi->uvd_dpm.soft_max_clk) {
+ pi->uvd_dpm.soft_min_clk =
+ pi->uvd_dpm.soft_max_clk;
+ ret = cz_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SetUvdSoftMin,
+ cz_get_uvd_level(adev,
+ pi->uvd_dpm.soft_min_clk,
+ PPSMC_MSG_SetUvdSoftMin));
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cz_dpm_uvd_force_lowest(struct amdgpu_device *adev)
+{
+ struct cz_power_info *pi = cz_get_pi(adev);
+ int ret = 0;
+
+ if (pi->uvd_dpm.soft_max_clk != pi->uvd_dpm.soft_min_clk) {
+ pi->uvd_dpm.soft_max_clk = pi->uvd_dpm.soft_min_clk;
+ ret = cz_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SetUvdSoftMax,
+ cz_get_uvd_level(adev,
+ pi->uvd_dpm.soft_max_clk,
+ PPSMC_MSG_SetUvdSoftMax));
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static uint32_t cz_dpm_get_max_uvd_level(struct amdgpu_device *adev)
+{
+ struct cz_power_info *pi = cz_get_pi(adev);
+
+ if (!pi->max_uvd_level) {
+ cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxUvdLevel);
+ pi->max_uvd_level = cz_get_argument(adev) + 1;
+ }
+
+ if (pi->max_uvd_level > CZ_MAX_HARDWARE_POWERLEVELS) {
+ DRM_ERROR("Invalid max uvd level!\n");
+ return -EINVAL;
+ }
+
+ return pi->max_uvd_level;
+}
+
+static int cz_dpm_unforce_uvd_dpm_levels(struct amdgpu_device *adev)
+{
+ struct cz_power_info *pi = cz_get_pi(adev);
+ struct amdgpu_uvd_clock_voltage_dependency_table *dep_table =
+ &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table;
+ uint32_t level = 0;
+ int ret = 0;
+
+ pi->uvd_dpm.soft_min_clk = dep_table->entries[0].vclk;
+ level = cz_dpm_get_max_uvd_level(adev) - 1;
+ if (level < dep_table->count)
+ pi->uvd_dpm.soft_max_clk = dep_table->entries[level].vclk;
+ else
+ pi->uvd_dpm.soft_max_clk =
+ dep_table->entries[dep_table->count - 1].vclk;
+
+ /* get min/max sclk soft value
+ * notify SMU to execute */
+ ret = cz_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SetUvdSoftMin,
+ cz_get_uvd_level(adev,
+ pi->uvd_dpm.soft_min_clk,
+ PPSMC_MSG_SetUvdSoftMin));
+ if (ret)
+ return ret;
+
+ ret = cz_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SetUvdSoftMax,
+ cz_get_uvd_level(adev,
+ pi->uvd_dpm.soft_max_clk,
+ PPSMC_MSG_SetUvdSoftMax));
+ if (ret)
+ return ret;
+
+ DRM_DEBUG("DPM uvd unforce state min=%d, max=%d.\n",
+ pi->uvd_dpm.soft_min_clk,
+ pi->uvd_dpm.soft_max_clk);
+
+ return 0;
+}
+
+static int cz_dpm_vce_force_highest(struct amdgpu_device *adev)
+{
+ struct cz_power_info *pi = cz_get_pi(adev);
+ int ret = 0;
+
+ if (pi->vce_dpm.soft_min_clk != pi->vce_dpm.soft_max_clk) {
+ pi->vce_dpm.soft_min_clk =
+ pi->vce_dpm.soft_max_clk;
+ ret = cz_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SetEclkSoftMin,
+ cz_get_eclk_level(adev,
+ pi->vce_dpm.soft_min_clk,
+ PPSMC_MSG_SetEclkSoftMin));
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cz_dpm_vce_force_lowest(struct amdgpu_device *adev)
+{
+ struct cz_power_info *pi = cz_get_pi(adev);
+ int ret = 0;
+
+ if (pi->vce_dpm.soft_max_clk != pi->vce_dpm.soft_min_clk) {
+ pi->vce_dpm.soft_max_clk = pi->vce_dpm.soft_min_clk;
+ ret = cz_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SetEclkSoftMax,
+ cz_get_uvd_level(adev,
+ pi->vce_dpm.soft_max_clk,
+ PPSMC_MSG_SetEclkSoftMax));
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static uint32_t cz_dpm_get_max_vce_level(struct amdgpu_device *adev)
+{
+ struct cz_power_info *pi = cz_get_pi(adev);
+
+ if (!pi->max_vce_level) {
+ cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxEclkLevel);
+ pi->max_vce_level = cz_get_argument(adev) + 1;
+ }
+
+ if (pi->max_vce_level > CZ_MAX_HARDWARE_POWERLEVELS) {
+ DRM_ERROR("Invalid max vce level!\n");
+ return -EINVAL;
+ }
+
+ return pi->max_vce_level;
+}
+
+static int cz_dpm_unforce_vce_dpm_levels(struct amdgpu_device *adev)
+{
+ struct cz_power_info *pi = cz_get_pi(adev);
+ struct amdgpu_vce_clock_voltage_dependency_table *dep_table =
+ &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
+ uint32_t level = 0;
+ int ret = 0;
+
+ pi->vce_dpm.soft_min_clk = dep_table->entries[0].ecclk;
+ level = cz_dpm_get_max_vce_level(adev) - 1;
+ if (level < dep_table->count)
+ pi->vce_dpm.soft_max_clk = dep_table->entries[level].ecclk;
+ else
+ pi->vce_dpm.soft_max_clk =
+ dep_table->entries[dep_table->count - 1].ecclk;
+
+ /* get min/max sclk soft value
+ * notify SMU to execute */
+ ret = cz_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SetEclkSoftMin,
+ cz_get_eclk_level(adev,
+ pi->vce_dpm.soft_min_clk,
+ PPSMC_MSG_SetEclkSoftMin));
+ if (ret)
+ return ret;
+
+ ret = cz_send_msg_to_smc_with_parameter(adev,
+ PPSMC_MSG_SetEclkSoftMax,
+ cz_get_eclk_level(adev,
+ pi->vce_dpm.soft_max_clk,
+ PPSMC_MSG_SetEclkSoftMax));
+ if (ret)
+ return ret;
+
+ DRM_DEBUG("DPM vce unforce state min=%d, max=%d.\n",
+ pi->vce_dpm.soft_min_clk,
+ pi->vce_dpm.soft_max_clk);
+
+ return 0;
+}
+
static int cz_dpm_force_dpm_level(struct amdgpu_device *adev,
enum amdgpu_dpm_forced_level level)
{
@@ -1746,25 +1971,70 @@ static int cz_dpm_force_dpm_level(struct amdgpu_device *adev,
switch (level) {
case AMDGPU_DPM_FORCED_LEVEL_HIGH:
+ /* sclk */
ret = cz_dpm_unforce_dpm_levels(adev);
if (ret)
return ret;
ret = cz_dpm_force_highest(adev);
if (ret)
return ret;
+
+ /* uvd */
+ ret = cz_dpm_unforce_uvd_dpm_levels(adev);
+ if (ret)
+ return ret;
+ ret = cz_dpm_uvd_force_highest(adev);
+ if (ret)
+ return ret;
+
+ /* vce */
+ ret = cz_dpm_unforce_vce_dpm_levels(adev);
+ if (ret)
+ return ret;
+ ret = cz_dpm_vce_force_highest(adev);
+ if (ret)
+ return ret;
break;
case AMDGPU_DPM_FORCED_LEVEL_LOW:
+ /* sclk */
ret = cz_dpm_unforce_dpm_levels(adev);
if (ret)
return ret;
ret = cz_dpm_force_lowest(adev);
if (ret)
return ret;
+
+ /* uvd */
+ ret = cz_dpm_unforce_uvd_dpm_levels(adev);
+ if (ret)
+ return ret;
+ ret = cz_dpm_uvd_force_lowest(adev);
+ if (ret)
+ return ret;
+
+ /* vce */
+ ret = cz_dpm_unforce_vce_dpm_levels(adev);
+ if (ret)
+ return ret;
+ ret = cz_dpm_vce_force_lowest(adev);
+ if (ret)
+ return ret;
break;
case AMDGPU_DPM_FORCED_LEVEL_AUTO:
+ /* sclk */
ret = cz_dpm_unforce_dpm_levels(adev);
if (ret)
return ret;
+
+ /* uvd */
+ ret = cz_dpm_unforce_uvd_dpm_levels(adev);
+ if (ret)
+ return ret;
+
+ /* vce */
+ ret = cz_dpm_unforce_vce_dpm_levels(adev);
+ if (ret)
+ return ret;
break;
default:
break;
@@ -1905,7 +2175,8 @@ static int cz_update_vce_dpm(struct amdgpu_device *adev)
pi->vce_dpm.hard_min_clk = table->entries[table->count-1].ecclk;
} else { /* non-stable p-state cases. without vce.Arbiter.EcclkHardMin */
- pi->vce_dpm.hard_min_clk = table->entries[0].ecclk;
+ /* leave it as set by user */
+ /*pi->vce_dpm.hard_min_clk = table->entries[0].ecclk;*/
}
cz_send_msg_to_smc_with_parameter(adev,
diff --git a/drivers/gpu/drm/amd/amdgpu/cz_dpm.h b/drivers/gpu/drm/amd/amdgpu/cz_dpm.h
index 99e1afc89629..5df8c1faab51 100644
--- a/drivers/gpu/drm/amd/amdgpu/cz_dpm.h
+++ b/drivers/gpu/drm/amd/amdgpu/cz_dpm.h
@@ -183,6 +183,8 @@ struct cz_power_info {
uint32_t voltage_drop_threshold;
uint32_t gfx_pg_threshold;
uint32_t max_sclk_level;
+ uint32_t max_uvd_level;
+ uint32_t max_vce_level;
/* flags */
bool didt_enabled;
bool video_start;
diff --git a/drivers/gpu/drm/amd/amdgpu/cz_ih.c b/drivers/gpu/drm/amd/amdgpu/cz_ih.c
index bc751bfbcae2..c79638f8e732 100644
--- a/drivers/gpu/drm/amd/amdgpu/cz_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/cz_ih.c
@@ -253,8 +253,14 @@ static void cz_ih_set_rptr(struct amdgpu_device *adev)
static int cz_ih_early_init(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ int ret;
+
+ ret = amdgpu_irq_add_domain(adev);
+ if (ret)
+ return ret;
cz_ih_set_interrupt_funcs(adev);
+
return 0;
}
@@ -278,6 +284,7 @@ static int cz_ih_sw_fini(void *handle)
amdgpu_irq_fini(adev);
amdgpu_ih_ring_fini(adev);
+ amdgpu_irq_remove_domain(adev);
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/iceland_ih.c b/drivers/gpu/drm/amd/amdgpu/iceland_ih.c
index 779532d350ff..679e7394a495 100644
--- a/drivers/gpu/drm/amd/amdgpu/iceland_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/iceland_ih.c
@@ -253,8 +253,14 @@ static void iceland_ih_set_rptr(struct amdgpu_device *adev)
static int iceland_ih_early_init(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ int ret;
+
+ ret = amdgpu_irq_add_domain(adev);
+ if (ret)
+ return ret;
iceland_ih_set_interrupt_funcs(adev);
+
return 0;
}
@@ -278,6 +284,7 @@ static int iceland_ih_sw_fini(void *handle)
amdgpu_irq_fini(adev);
amdgpu_ih_ring_fini(adev);
+ amdgpu_irq_remove_domain(adev);
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
index 743c372837aa..b6f7d7bff929 100644
--- a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
@@ -273,8 +273,14 @@ static void tonga_ih_set_rptr(struct amdgpu_device *adev)
static int tonga_ih_early_init(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ int ret;
+
+ ret = amdgpu_irq_add_domain(adev);
+ if (ret)
+ return ret;
tonga_ih_set_interrupt_funcs(adev);
+
return 0;
}
@@ -301,6 +307,7 @@ static int tonga_ih_sw_fini(void *handle)
amdgpu_irq_fini(adev);
amdgpu_ih_ring_fini(adev);
+ amdgpu_irq_add_domain(adev);
return 0;
}
diff --git a/drivers/gpu/drm/amd/include/cgs_common.h b/drivers/gpu/drm/amd/include/cgs_common.h
index 03affb348f69..713aec954692 100644
--- a/drivers/gpu/drm/amd/include/cgs_common.h
+++ b/drivers/gpu/drm/amd/include/cgs_common.h
@@ -122,6 +122,17 @@ struct cgs_system_info {
uint64_t padding[13];
};
+/*
+ * enum cgs_resource_type - GPU resource type
+ */
+enum cgs_resource_type {
+ CGS_RESOURCE_TYPE_MMIO = 0,
+ CGS_RESOURCE_TYPE_FB,
+ CGS_RESOURCE_TYPE_IO,
+ CGS_RESOURCE_TYPE_DOORBELL,
+ CGS_RESOURCE_TYPE_ROM,
+};
+
/**
* struct cgs_clock_limits - Clock limits
*
@@ -417,6 +428,23 @@ typedef void (*cgs_write_pci_config_word_t)(void *cgs_device, unsigned addr,
typedef void (*cgs_write_pci_config_dword_t)(void *cgs_device, unsigned addr,
uint32_t value);
+
+/**
+ * cgs_get_pci_resource() - provide access to a device resource (PCI BAR)
+ * @cgs_device: opaque device handle
+ * @resource_type: Type of Resource (MMIO, IO, ROM, FB, DOORBELL)
+ * @size: size of the region
+ * @offset: offset from the start of the region
+ * @resource_base: base address (not including offset) returned
+ *
+ * Return: 0 on success, -errno otherwise
+ */
+typedef int (*cgs_get_pci_resource_t)(void *cgs_device,
+ enum cgs_resource_type resource_type,
+ uint64_t size,
+ uint64_t offset,
+ uint64_t *resource_base);
+
/**
* cgs_atom_get_data_table() - Get a pointer to an ATOM BIOS data table
* @cgs_device: opaque device handle
@@ -593,6 +621,8 @@ struct cgs_ops {
cgs_write_pci_config_byte_t write_pci_config_byte;
cgs_write_pci_config_word_t write_pci_config_word;
cgs_write_pci_config_dword_t write_pci_config_dword;
+ /* PCI resources */
+ cgs_get_pci_resource_t get_pci_resource;
/* ATOM BIOS */
cgs_atom_get_data_table_t atom_get_data_table;
cgs_atom_get_cmd_table_revs_t atom_get_cmd_table_revs;
@@ -708,5 +738,9 @@ struct cgs_device
CGS_CALL(call_acpi_method, dev, acpi_method, acpi_function, pintput, poutput, output_count, input_size, output_size)
#define cgs_query_system_info(dev, sys_info) \
CGS_CALL(query_system_info, dev, sys_info)
+#define cgs_get_pci_resource(cgs_device, resource_type, size, offset, \
+ resource_base) \
+ CGS_CALL(get_pci_resource, cgs_device, resource_type, size, offset, \
+ resource_base)
#endif /* _CGS_COMMON_H */
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c
index d166fd925dbb..ebdb43a8daef 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c
@@ -199,7 +199,7 @@ static int tonga_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg)
PP_ASSERT_WITH_CODE(
1 == SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP),
"Failed to send Previous Message.",
- return 1);
+ );
cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
@@ -207,7 +207,7 @@ static int tonga_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg)
PP_ASSERT_WITH_CODE(
1 == SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP),
"Failed to send Message.",
- return 1);
+ );
return 0;
}
@@ -229,7 +229,7 @@ static int tonga_send_msg_to_smc_without_waiting
PP_ASSERT_WITH_CODE(
1 == SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP),
"Failed to send Previous Message.",
- return 0);
+ );
cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
return 0;
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
index 8d6f859f8200..9f77c3b94cc6 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
@@ -305,8 +305,10 @@ int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu,
mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, NULL);
if (!mapping) {
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
- if (!mapping)
- return -ENOMEM;
+ if (!mapping) {
+ ret = -ENOMEM;
+ goto out;
+ }
INIT_LIST_HEAD(&mapping->scan_node);
mapping->object = etnaviv_obj;
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
index d39093dc37e6..056a72e6ed26 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
@@ -251,9 +251,12 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
chipIdentity = gpu_read(gpu, VIVS_HI_CHIP_IDENTITY);
/* Special case for older graphic cores. */
- if (VIVS_HI_CHIP_IDENTITY_FAMILY(chipIdentity) == 0x01) {
+ if (((chipIdentity & VIVS_HI_CHIP_IDENTITY_FAMILY__MASK)
+ >> VIVS_HI_CHIP_IDENTITY_FAMILY__SHIFT) == 0x01) {
gpu->identity.model = 0x500; /* gc500 */
- gpu->identity.revision = VIVS_HI_CHIP_IDENTITY_REVISION(chipIdentity);
+ gpu->identity.revision =
+ (chipIdentity & VIVS_HI_CHIP_IDENTITY_REVISION__MASK)
+ >> VIVS_HI_CHIP_IDENTITY_REVISION__SHIFT;
} else {
gpu->identity.model = gpu_read(gpu, VIVS_HI_CHIP_MODEL);
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.h b/drivers/gpu/drm/nouveau/nouveau_debugfs.h
index 52c7161297d7..b8c03ff5bf05 100644
--- a/drivers/gpu/drm/nouveau/nouveau_debugfs.h
+++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.h
@@ -34,13 +34,13 @@ nouveau_drm_debugfs_cleanup(struct drm_minor *minor)
}
static inline int
-nouveau_debugfs_init(struct nouveau_drm *)
+nouveau_debugfs_init(struct nouveau_drm *drm)
{
return 0;
}
static inline void
-nouveau_debugfs_fini(struct nouveau_drm *)
+nouveau_debugfs_fini(struct nouveau_drm *drm)
{
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c
index b32954f5311e..d71e5db5028a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c
@@ -119,7 +119,7 @@ nvkm_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width)
struct pci_bus *pbus;
int ret;
- if (pci || !pci_is_pcie(pci->pdev))
+ if (!pci || !pci_is_pcie(pci->pdev))
return 0;
pbus = pci->pdev->bus;
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 59abebd6b5dc..460c8f2989da 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -713,7 +713,7 @@ static struct attribute *hwmon_attributes[] = {
static umode_t hwmon_attributes_visible(struct kobject *kobj,
struct attribute *attr, int index)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct radeon_device *rdev = dev_get_drvdata(dev);
umode_t effective_mode = attr->mode;
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6867d3..85739859dffc 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,13 @@ config ROCKCHIP_DW_HDMI
for the Synopsys DesignWare HDMI driver. If you want to
enable HDMI on RK3288 based SoC, you should selet this
option.
+
+config ROCKCHIP_DW_MIPI_DSI
+ tristate "Rockchip specific extensions for Synopsys DW MIPI DSI"
+ depends on DRM_ROCKCHIP
+ select DRM_MIPI_DSI
+ help
+ This selects support for Rockchip SoC specific extensions
+ for the Synopsys DesignWare HDMI driver. If you want to
+ enable MIPI DSI on RK3288 based SoC, you should selet this
+ option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a9d380f3a423..d1dc0f7b01db 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
rockchip_drm_gem.o
obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
new file mode 100644
index 000000000000..7bfe243c6173
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drmP.h>
+#include <video/mipi_display.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#define DRIVER_NAME "dw-mipi-dsi"
+
+#define GRF_SOC_CON6 0x025c
+#define DSI0_SEL_VOP_LIT (1 << 6)
+#define DSI1_SEL_VOP_LIT (1 << 9)
+
+#define DSI_VERSION 0x00
+#define DSI_PWR_UP 0x04
+#define RESET 0
+#define POWERUP BIT(0)
+
+#define DSI_CLKMGR_CFG 0x08
+#define TO_CLK_DIVIDSION(div) (((div) & 0xff) << 8)
+#define TX_ESC_CLK_DIVIDSION(div) (((div) & 0xff) << 0)
+
+#define DSI_DPI_VCID 0x0c
+#define DPI_VID(vid) (((vid) & 0x3) << 0)
+
+#define DSI_DPI_COLOR_CODING 0x10
+#define EN18_LOOSELY BIT(8)
+#define DPI_COLOR_CODING_16BIT_1 0x0
+#define DPI_COLOR_CODING_16BIT_2 0x1
+#define DPI_COLOR_CODING_16BIT_3 0x2
+#define DPI_COLOR_CODING_18BIT_1 0x3
+#define DPI_COLOR_CODING_18BIT_2 0x4
+#define DPI_COLOR_CODING_24BIT 0x5
+
+#define DSI_DPI_CFG_POL 0x14
+#define COLORM_ACTIVE_LOW BIT(4)
+#define SHUTD_ACTIVE_LOW BIT(3)
+#define HSYNC_ACTIVE_LOW BIT(2)
+#define VSYNC_ACTIVE_LOW BIT(1)
+#define DATAEN_ACTIVE_LOW BIT(0)
+
+#define DSI_DPI_LP_CMD_TIM 0x18
+#define OUTVACT_LPCMD_TIME(p) (((p) & 0xff) << 16)
+#define INVACT_LPCMD_TIME(p) ((p) & 0xff)
+
+#define DSI_DBI_CFG 0x20
+#define DSI_DBI_CMDSIZE 0x28
+
+#define DSI_PCKHDL_CFG 0x2c
+#define EN_CRC_RX BIT(4)
+#define EN_ECC_RX BIT(3)
+#define EN_BTA BIT(2)
+#define EN_EOTP_RX BIT(1)
+#define EN_EOTP_TX BIT(0)
+
+#define DSI_MODE_CFG 0x34
+#define ENABLE_VIDEO_MODE 0
+#define ENABLE_CMD_MODE BIT(0)
+
+#define DSI_VID_MODE_CFG 0x38
+#define FRAME_BTA_ACK BIT(14)
+#define ENABLE_LOW_POWER (0x3f << 8)
+#define ENABLE_LOW_POWER_MASK (0x3f << 8)
+#define VID_MODE_TYPE_BURST_SYNC_PULSES 0x2
+#define VID_MODE_TYPE_MASK 0x3
+
+#define DSI_VID_PKT_SIZE 0x3c
+#define VID_PKT_SIZE(p) (((p) & 0x3fff) << 0)
+#define VID_PKT_MAX_SIZE 0x3fff
+
+#define DSI_VID_HSA_TIME 0x48
+#define DSI_VID_HBP_TIME 0x4c
+#define DSI_VID_HLINE_TIME 0x50
+#define DSI_VID_VSA_LINES 0x54
+#define DSI_VID_VBP_LINES 0x58
+#define DSI_VID_VFP_LINES 0x5c
+#define DSI_VID_VACTIVE_LINES 0x60
+#define DSI_CMD_MODE_CFG 0x68
+#define MAX_RD_PKT_SIZE_LP BIT(24)
+#define DCS_LW_TX_LP BIT(19)
+#define DCS_SR_0P_TX_LP BIT(18)
+#define DCS_SW_1P_TX_LP BIT(17)
+#define DCS_SW_0P_TX_LP BIT(16)
+#define GEN_LW_TX_LP BIT(14)
+#define GEN_SR_2P_TX_LP BIT(13)
+#define GEN_SR_1P_TX_LP BIT(12)
+#define GEN_SR_0P_TX_LP BIT(11)
+#define GEN_SW_2P_TX_LP BIT(10)
+#define GEN_SW_1P_TX_LP BIT(9)
+#define GEN_SW_0P_TX_LP BIT(8)
+#define EN_ACK_RQST BIT(1)
+#define EN_TEAR_FX BIT(0)
+
+#define CMD_MODE_ALL_LP (MAX_RD_PKT_SIZE_LP | \
+ DCS_LW_TX_LP | \
+ DCS_SR_0P_TX_LP | \
+ DCS_SW_1P_TX_LP | \
+ DCS_SW_0P_TX_LP | \
+ GEN_LW_TX_LP | \
+ GEN_SR_2P_TX_LP | \
+ GEN_SR_1P_TX_LP | \
+ GEN_SR_0P_TX_LP | \
+ GEN_SW_2P_TX_LP | \
+ GEN_SW_1P_TX_LP | \
+ GEN_SW_0P_TX_LP)
+
+#define DSI_GEN_HDR 0x6c
+#define GEN_HDATA(data) (((data) & 0xffff) << 8)
+#define GEN_HDATA_MASK (0xffff << 8)
+#define GEN_HTYPE(type) (((type) & 0xff) << 0)
+#define GEN_HTYPE_MASK 0xff
+
+#define DSI_GEN_PLD_DATA 0x70
+
+#define DSI_CMD_PKT_STATUS 0x74
+#define GEN_CMD_EMPTY BIT(0)
+#define GEN_CMD_FULL BIT(1)
+#define GEN_PLD_W_EMPTY BIT(2)
+#define GEN_PLD_W_FULL BIT(3)
+#define GEN_PLD_R_EMPTY BIT(4)
+#define GEN_PLD_R_FULL BIT(5)
+#define GEN_RD_CMD_BUSY BIT(6)
+
+#define DSI_TO_CNT_CFG 0x78
+#define HSTX_TO_CNT(p) (((p) & 0xffff) << 16)
+#define LPRX_TO_CNT(p) ((p) & 0xffff)
+
+#define DSI_BTA_TO_CNT 0x8c
+
+#define DSI_LPCLK_CTRL 0x94
+#define AUTO_CLKLANE_CTRL BIT(1)
+#define PHY_TXREQUESTCLKHS BIT(0)
+
+#define DSI_PHY_TMR_LPCLK_CFG 0x98
+#define PHY_CLKHS2LP_TIME(lbcc) (((lbcc) & 0x3ff) << 16)
+#define PHY_CLKLP2HS_TIME(lbcc) ((lbcc) & 0x3ff)
+
+#define DSI_PHY_TMR_CFG 0x9c
+#define PHY_HS2LP_TIME(lbcc) (((lbcc) & 0xff) << 24)
+#define PHY_LP2HS_TIME(lbcc) (((lbcc) & 0xff) << 16)
+#define MAX_RD_TIME(lbcc) ((lbcc) & 0x7fff)
+
+#define DSI_PHY_RSTZ 0xa0
+#define PHY_DISFORCEPLL 0
+#define PHY_ENFORCEPLL BIT(3)
+#define PHY_DISABLECLK 0
+#define PHY_ENABLECLK BIT(2)
+#define PHY_RSTZ 0
+#define PHY_UNRSTZ BIT(1)
+#define PHY_SHUTDOWNZ 0
+#define PHY_UNSHUTDOWNZ BIT(0)
+
+#define DSI_PHY_IF_CFG 0xa4
+#define N_LANES(n) ((((n) - 1) & 0x3) << 0)
+#define PHY_STOP_WAIT_TIME(cycle) (((cycle) & 0xff) << 8)
+
+#define DSI_PHY_STATUS 0xb0
+#define LOCK BIT(0)
+#define STOP_STATE_CLK_LANE BIT(2)
+
+#define DSI_PHY_TST_CTRL0 0xb4
+#define PHY_TESTCLK BIT(1)
+#define PHY_UNTESTCLK 0
+#define PHY_TESTCLR BIT(0)
+#define PHY_UNTESTCLR 0
+
+#define DSI_PHY_TST_CTRL1 0xb8
+#define PHY_TESTEN BIT(16)
+#define PHY_UNTESTEN 0
+#define PHY_TESTDOUT(n) (((n) & 0xff) << 8)
+#define PHY_TESTDIN(n) (((n) & 0xff) << 0)
+
+#define DSI_INT_ST0 0xbc
+#define DSI_INT_ST1 0xc0
+#define DSI_INT_MSK0 0xc4
+#define DSI_INT_MSK1 0xc8
+
+#define PHY_STATUS_TIMEOUT_US 10000
+#define CMD_PKT_STATUS_TIMEOUT_US 20000
+
+#define BYPASS_VCO_RANGE BIT(7)
+#define VCO_RANGE_CON_SEL(val) (((val) & 0x7) << 3)
+#define VCO_IN_CAP_CON_DEFAULT (0x0 << 1)
+#define VCO_IN_CAP_CON_LOW (0x1 << 1)
+#define VCO_IN_CAP_CON_HIGH (0x2 << 1)
+#define REF_BIAS_CUR_SEL BIT(0)
+
+#define CP_CURRENT_3MA BIT(3)
+#define CP_PROGRAM_EN BIT(7)
+#define LPF_PROGRAM_EN BIT(6)
+#define LPF_RESISTORS_20_KOHM 0
+
+#define HSFREQRANGE_SEL(val) (((val) & 0x3f) << 1)
+
+#define INPUT_DIVIDER(val) ((val - 1) & 0x7f)
+#define LOW_PROGRAM_EN 0
+#define HIGH_PROGRAM_EN BIT(7)
+#define LOOP_DIV_LOW_SEL(val) ((val - 1) & 0x1f)
+#define LOOP_DIV_HIGH_SEL(val) (((val - 1) >> 5) & 0x1f)
+#define PLL_LOOP_DIV_EN BIT(5)
+#define PLL_INPUT_DIV_EN BIT(4)
+
+#define POWER_CONTROL BIT(6)
+#define INTERNAL_REG_CURRENT BIT(3)
+#define BIAS_BLOCK_ON BIT(2)
+#define BANDGAP_ON BIT(0)
+
+#define TER_RESISTOR_HIGH BIT(7)
+#define TER_RESISTOR_LOW 0
+#define LEVEL_SHIFTERS_ON BIT(6)
+#define TER_CAL_DONE BIT(5)
+#define SETRD_MAX (0x7 << 2)
+#define POWER_MANAGE BIT(1)
+#define TER_RESISTORS_ON BIT(0)
+
+#define BIASEXTR_SEL(val) ((val) & 0x7)
+#define BANDGAP_SEL(val) ((val) & 0x7)
+#define TLP_PROGRAM_EN BIT(7)
+#define THS_PRE_PROGRAM_EN BIT(7)
+#define THS_ZERO_PROGRAM_EN BIT(6)
+
+enum {
+ BANDGAP_97_07,
+ BANDGAP_98_05,
+ BANDGAP_99_02,
+ BANDGAP_100_00,
+ BANDGAP_93_17,
+ BANDGAP_94_15,
+ BANDGAP_95_12,
+ BANDGAP_96_10,
+};
+
+enum {
+ BIASEXTR_87_1,
+ BIASEXTR_91_5,
+ BIASEXTR_95_9,
+ BIASEXTR_100,
+ BIASEXTR_105_94,
+ BIASEXTR_111_88,
+ BIASEXTR_118_8,
+ BIASEXTR_127_7,
+};
+
+struct dw_mipi_dsi_plat_data {
+ unsigned int max_data_lanes;
+ enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+ struct drm_display_mode *mode);
+};
+
+struct dw_mipi_dsi {
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+ struct mipi_dsi_host dsi_host;
+ struct drm_panel *panel;
+ struct device *dev;
+ struct regmap *grf_regmap;
+ void __iomem *base;
+
+ struct clk *pllref_clk;
+ struct clk *pclk;
+
+ unsigned int lane_mbps; /* per lane */
+ u32 channel;
+ u32 lanes;
+ u32 format;
+ u16 input_div;
+ u16 feedback_div;
+ struct drm_display_mode *mode;
+
+ const struct dw_mipi_dsi_plat_data *pdata;
+};
+
+enum dw_mipi_dsi_mode {
+ DW_MIPI_DSI_CMD_MODE,
+ DW_MIPI_DSI_VID_MODE,
+};
+
+struct dphy_pll_testdin_map {
+ unsigned int max_mbps;
+ u8 testdin;
+};
+
+/* The table is based on 27MHz DPHY pll reference clock. */
+static const struct dphy_pll_testdin_map dptdin_map[] = {
+ { 90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
+ { 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
+ { 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
+ { 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
+ { 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
+ { 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
+ { 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
+ {1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
+ {1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
+ {1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
+};
+
+static int max_mbps_to_testdin(unsigned int max_mbps)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dptdin_map); i++)
+ if (dptdin_map[i].max_mbps > max_mbps)
+ return dptdin_map[i].testdin;
+
+ return -EINVAL;
+}
+
+/*
+ * The controller should generate 2 frames before
+ * preparing the peripheral.
+ */
+static void dw_mipi_dsi_wait_for_two_frames(struct dw_mipi_dsi *dsi)
+{
+ int refresh, two_frames;
+
+ refresh = drm_mode_vrefresh(dsi->mode);
+ two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
+ msleep(two_frames);
+}
+
+static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
+{
+ return container_of(host, struct dw_mipi_dsi, dsi_host);
+}
+
+static inline struct dw_mipi_dsi *con_to_dsi(struct drm_connector *con)
+{
+ return container_of(con, struct dw_mipi_dsi, connector);
+}
+
+static inline struct dw_mipi_dsi *encoder_to_dsi(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct dw_mipi_dsi, encoder);
+}
+static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
+{
+ writel(val, dsi->base + reg);
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg)
+{
+ return readl(dsi->base + reg);
+}
+
+static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 test_code,
+ u8 test_data)
+{
+ /*
+ * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+ * is latched internally as the current test code. Test data is
+ * programmed internally by rising edge on TESTCLK.
+ */
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
+ PHY_TESTDIN(test_code));
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
+ PHY_TESTDIN(test_data));
+
+ dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+}
+
+static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
+{
+ int ret, testdin, vco, val;
+
+ vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
+
+ testdin = max_mbps_to_testdin(dsi->lane_mbps);
+ if (testdin < 0) {
+ dev_err(dsi->dev,
+ "failed to get testdin for %dmbps lane clock\n",
+ dsi->lane_mbps);
+ return testdin;
+ }
+
+ dsi_write(dsi, DSI_PWR_UP, POWERUP);
+
+ dw_mipi_dsi_phy_write(dsi, 0x10, BYPASS_VCO_RANGE |
+ VCO_RANGE_CON_SEL(vco) |
+ VCO_IN_CAP_CON_LOW |
+ REF_BIAS_CUR_SEL);
+
+ dw_mipi_dsi_phy_write(dsi, 0x11, CP_CURRENT_3MA);
+ dw_mipi_dsi_phy_write(dsi, 0x12, CP_PROGRAM_EN | LPF_PROGRAM_EN |
+ LPF_RESISTORS_20_KOHM);
+
+ dw_mipi_dsi_phy_write(dsi, 0x44, HSFREQRANGE_SEL(testdin));
+
+ dw_mipi_dsi_phy_write(dsi, 0x19, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+ dw_mipi_dsi_phy_write(dsi, 0x17, INPUT_DIVIDER(dsi->input_div));
+ dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_LOW_SEL(dsi->feedback_div) |
+ LOW_PROGRAM_EN);
+ dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
+ HIGH_PROGRAM_EN);
+
+ dw_mipi_dsi_phy_write(dsi, 0x20, POWER_CONTROL | INTERNAL_REG_CURRENT |
+ BIAS_BLOCK_ON | BANDGAP_ON);
+
+ dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_LOW | TER_CAL_DONE |
+ SETRD_MAX | TER_RESISTORS_ON);
+ dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
+ SETRD_MAX | POWER_MANAGE |
+ TER_RESISTORS_ON);
+
+ dw_mipi_dsi_phy_write(dsi, 0x22, LOW_PROGRAM_EN |
+ BIASEXTR_SEL(BIASEXTR_127_7));
+ dw_mipi_dsi_phy_write(dsi, 0x22, HIGH_PROGRAM_EN |
+ BANDGAP_SEL(BANDGAP_96_10));
+
+ dw_mipi_dsi_phy_write(dsi, 0x70, TLP_PROGRAM_EN | 0xf);
+ dw_mipi_dsi_phy_write(dsi, 0x71, THS_PRE_PROGRAM_EN | 0x55);
+ dw_mipi_dsi_phy_write(dsi, 0x72, THS_ZERO_PROGRAM_EN | 0xa);
+
+ dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
+ PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
+
+
+ ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
+ val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(dsi->dev, "failed to wait for phy lock state\n");
+ return ret;
+ }
+
+ ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
+ val, val & STOP_STATE_CLK_LANE, 1000,
+ PHY_STATUS_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(dsi->dev,
+ "failed to wait for phy clk lane stop state\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi)
+{
+ unsigned int bpp, i, pre;
+ unsigned long mpclk, pllref, tmp;
+ unsigned int m = 1, n = 1, target_mbps = 1000;
+ unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps;
+
+ bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+ if (bpp < 0) {
+ dev_err(dsi->dev, "failed to get bpp for pixel format %d\n",
+ dsi->format);
+ return bpp;
+ }
+
+ mpclk = DIV_ROUND_UP(dsi->mode->clock, MSEC_PER_SEC);
+ if (mpclk) {
+ /* take 1 / 0.9, since mbps must big than bandwidth of RGB */
+ tmp = mpclk * (bpp / dsi->lanes) * 10 / 9;
+ if (tmp < max_mbps)
+ target_mbps = tmp;
+ else
+ dev_err(dsi->dev, "DPHY clock frequency is out of range\n");
+ }
+
+ pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
+ tmp = pllref;
+
+ for (i = 1; i < 6; i++) {
+ pre = pllref / i;
+ if ((tmp > (target_mbps % pre)) && (target_mbps / pre < 512)) {
+ tmp = target_mbps % pre;
+ n = i;
+ m = target_mbps / pre;
+ }
+ if (tmp == 0)
+ break;
+ }
+
+ dsi->lane_mbps = pllref / n * m;
+ dsi->input_div = n;
+ dsi->feedback_div = m;
+
+ return 0;
+}
+
+static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+{
+ struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+ if (device->lanes > dsi->pdata->max_data_lanes) {
+ dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
+ device->lanes);
+ return -EINVAL;
+ }
+
+ if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) ||
+ !(device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)) {
+ dev_err(dsi->dev, "device mode is unsupported\n");
+ return -EINVAL;
+ }
+
+ dsi->lanes = device->lanes;
+ dsi->channel = device->channel;
+ dsi->format = device->format;
+ dsi->panel = of_drm_find_panel(device->dev.of_node);
+ if (dsi->panel)
+ return drm_panel_attach(dsi->panel, &dsi->connector);
+
+ return -EINVAL;
+}
+
+static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+{
+ struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+ drm_panel_detach(dsi->panel);
+
+ return 0;
+}
+
+static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 val)
+{
+ int ret;
+
+ ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+ val, !(val & GEN_CMD_FULL), 1000,
+ CMD_PKT_STATUS_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(dsi->dev, "failed to get available command FIFO\n");
+ return ret;
+ }
+
+ dsi_write(dsi, DSI_GEN_HDR, val);
+
+ ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+ val, val & (GEN_CMD_EMPTY | GEN_PLD_W_EMPTY),
+ 1000, CMD_PKT_STATUS_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(dsi->dev, "failed to write command FIFO\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
+ const struct mipi_dsi_msg *msg)
+{
+ const u16 *tx_buf = msg->tx_buf;
+ u32 val = GEN_HDATA(*tx_buf) | GEN_HTYPE(msg->type);
+
+ if (msg->tx_len > 2) {
+ dev_err(dsi->dev, "too long tx buf length %zu for short write\n",
+ msg->tx_len);
+ return -EINVAL;
+ }
+
+ return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
+}
+
+static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
+ const struct mipi_dsi_msg *msg)
+{
+ const u32 *tx_buf = msg->tx_buf;
+ int len = msg->tx_len, pld_data_bytes = sizeof(*tx_buf), ret;
+ u32 val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type);
+ u32 remainder = 0;
+
+ if (msg->tx_len < 3) {
+ dev_err(dsi->dev, "wrong tx buf length %zu for long write\n",
+ msg->tx_len);
+ return -EINVAL;
+ }
+
+ while (DIV_ROUND_UP(len, pld_data_bytes)) {
+ if (len < pld_data_bytes) {
+ memcpy(&remainder, tx_buf, len);
+ dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
+ len = 0;
+ } else {
+ dsi_write(dsi, DSI_GEN_PLD_DATA, *tx_buf);
+ tx_buf++;
+ len -= pld_data_bytes;
+ }
+
+ ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+ val, !(val & GEN_PLD_W_FULL), 1000,
+ CMD_PKT_STATUS_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(dsi->dev,
+ "failed to get available write payload FIFO\n");
+ return ret;
+ }
+ }
+
+ return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
+}
+
+static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
+ const struct mipi_dsi_msg *msg)
+{
+ struct dw_mipi_dsi *dsi = host_to_dsi(host);
+ int ret;
+
+ switch (msg->type) {
+ case MIPI_DSI_DCS_SHORT_WRITE:
+ case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+ case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+ ret = dw_mipi_dsi_dcs_short_write(dsi, msg);
+ break;
+ case MIPI_DSI_DCS_LONG_WRITE:
+ ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
+ break;
+ default:
+ dev_err(dsi->dev, "unsupported message type\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
+ .attach = dw_mipi_dsi_host_attach,
+ .detach = dw_mipi_dsi_host_detach,
+ .transfer = dw_mipi_dsi_host_transfer,
+};
+
+static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
+{
+ u32 val;
+
+ val = VID_MODE_TYPE_BURST_SYNC_PULSES | ENABLE_LOW_POWER;
+
+ dsi_write(dsi, DSI_VID_MODE_CFG, val);
+}
+
+static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
+ enum dw_mipi_dsi_mode mode)
+{
+ if (mode == DW_MIPI_DSI_CMD_MODE) {
+ dsi_write(dsi, DSI_PWR_UP, RESET);
+ dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+ dsi_write(dsi, DSI_PWR_UP, POWERUP);
+ } else {
+ dsi_write(dsi, DSI_PWR_UP, RESET);
+ dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
+ dw_mipi_dsi_video_mode_config(dsi);
+ dsi_write(dsi, DSI_PWR_UP, POWERUP);
+ }
+}
+
+static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
+{
+ dsi_write(dsi, DSI_PWR_UP, RESET);
+ dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ);
+}
+
+static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
+{
+ dsi_write(dsi, DSI_PWR_UP, RESET);
+ dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK
+ | PHY_RSTZ | PHY_SHUTDOWNZ);
+ dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
+ TX_ESC_CLK_DIVIDSION(7));
+ dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
+}
+
+static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
+ struct drm_display_mode *mode)
+{
+ u32 val = 0, color = 0;
+
+ switch (dsi->format) {
+ case MIPI_DSI_FMT_RGB888:
+ color = DPI_COLOR_CODING_24BIT;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ color = DPI_COLOR_CODING_18BIT_1;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ color = DPI_COLOR_CODING_16BIT_1;
+ break;
+ }
+
+ if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
+ val |= VSYNC_ACTIVE_LOW;
+ if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
+ val |= HSYNC_ACTIVE_LOW;
+
+ dsi_write(dsi, DSI_DPI_VCID, DPI_VID(dsi->channel));
+ dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
+ dsi_write(dsi, DSI_DPI_CFG_POL, val);
+ dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
+ | INVACT_LPCMD_TIME(4));
+}
+
+static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
+{
+ dsi_write(dsi, DSI_PCKHDL_CFG, EN_CRC_RX | EN_ECC_RX | EN_BTA);
+}
+
+static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
+ struct drm_display_mode *mode)
+{
+ dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
+}
+
+static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
+{
+ dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+ dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
+ dsi_write(dsi, DSI_CMD_MODE_CFG, CMD_MODE_ALL_LP);
+ dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+}
+
+/* Get lane byte clock cycles. */
+static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
+ u32 hcomponent)
+{
+ u32 frac, lbcc;
+
+ lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
+
+ frac = lbcc % dsi->mode->clock;
+ lbcc = lbcc / dsi->mode->clock;
+ if (frac)
+ lbcc++;
+
+ return lbcc;
+}
+
+static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi)
+{
+ u32 htotal, hsa, hbp, lbcc;
+ struct drm_display_mode *mode = dsi->mode;
+
+ htotal = mode->htotal;
+ hsa = mode->hsync_end - mode->hsync_start;
+ hbp = mode->htotal - mode->hsync_end;
+
+ lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, htotal);
+ dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
+
+ lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, hsa);
+ dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
+
+ lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, hbp);
+ dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
+}
+
+static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi)
+{
+ u32 vactive, vsa, vfp, vbp;
+ struct drm_display_mode *mode = dsi->mode;
+
+ vactive = mode->vdisplay;
+ vsa = mode->vsync_end - mode->vsync_start;
+ vfp = mode->vsync_start - mode->vdisplay;
+ vbp = mode->vtotal - mode->vsync_end;
+
+ dsi_write(dsi, DSI_VID_VACTIVE_LINES, vactive);
+ dsi_write(dsi, DSI_VID_VSA_LINES, vsa);
+ dsi_write(dsi, DSI_VID_VFP_LINES, vfp);
+ dsi_write(dsi, DSI_VID_VBP_LINES, vbp);
+}
+
+static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
+{
+ dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40)
+ | PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
+
+ dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
+ | PHY_CLKLP2HS_TIME(0x40));
+}
+
+static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
+{
+ dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
+ N_LANES(dsi->lanes));
+}
+
+static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
+{
+ dsi_read(dsi, DSI_INT_ST0);
+ dsi_read(dsi, DSI_INT_ST1);
+ dsi_write(dsi, DSI_INT_MSK0, 0);
+ dsi_write(dsi, DSI_INT_MSK1, 0);
+}
+
+static void dw_mipi_dsi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+ int ret;
+
+ dsi->mode = adjusted_mode;
+
+ ret = dw_mipi_dsi_get_lane_bps(dsi);
+ if (ret < 0)
+ return;
+
+ if (clk_prepare_enable(dsi->pclk)) {
+ dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+ return;
+ }
+
+ dw_mipi_dsi_init(dsi);
+ dw_mipi_dsi_dpi_config(dsi, mode);
+ dw_mipi_dsi_packet_handler_config(dsi);
+ dw_mipi_dsi_video_mode_config(dsi);
+ dw_mipi_dsi_video_packet_config(dsi, mode);
+ dw_mipi_dsi_command_mode_config(dsi);
+ dw_mipi_dsi_line_timer_config(dsi);
+ dw_mipi_dsi_vertical_timing_config(dsi);
+ dw_mipi_dsi_dphy_timing_config(dsi);
+ dw_mipi_dsi_dphy_interface_config(dsi);
+ dw_mipi_dsi_clear_err(dsi);
+ if (drm_panel_prepare(dsi->panel))
+ dev_err(dsi->dev, "failed to prepare panel\n");
+
+ clk_disable_unprepare(dsi->pclk);
+}
+
+static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+
+ drm_panel_disable(dsi->panel);
+
+ if (clk_prepare_enable(dsi->pclk)) {
+ dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+ return;
+ }
+
+ dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
+ drm_panel_unprepare(dsi->panel);
+ dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
+
+ /*
+ * This is necessary to make sure the peripheral will be driven
+ * normally when the display is enabled again later.
+ */
+ msleep(120);
+
+ dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
+ dw_mipi_dsi_disable(dsi);
+ clk_disable_unprepare(dsi->pclk);
+}
+
+static bool dw_mipi_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
+{
+ struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+ int mux = rockchip_drm_encoder_get_mux_id(dsi->dev->of_node, encoder);
+ u32 interface_pix_fmt;
+ u32 val;
+
+ if (clk_prepare_enable(dsi->pclk)) {
+ dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+ return;
+ }
+
+ dw_mipi_dsi_phy_init(dsi);
+ dw_mipi_dsi_wait_for_two_frames(dsi);
+
+ dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
+ drm_panel_enable(dsi->panel);
+
+ clk_disable_unprepare(dsi->pclk);
+
+ switch (dsi->format) {
+ case MIPI_DSI_FMT_RGB888:
+ interface_pix_fmt = ROCKCHIP_OUT_MODE_P888;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ interface_pix_fmt = ROCKCHIP_OUT_MODE_P666;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ interface_pix_fmt = ROCKCHIP_OUT_MODE_P565;
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+
+ rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_DSI,
+ interface_pix_fmt);
+
+ if (mux)
+ val = DSI0_SEL_VOP_LIT | (DSI0_SEL_VOP_LIT << 16);
+ else
+ val = DSI0_SEL_VOP_LIT << 16;
+
+ regmap_write(dsi->grf_regmap, GRF_SOC_CON6, val);
+ dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
+}
+
+static struct drm_encoder_helper_funcs
+dw_mipi_dsi_encoder_helper_funcs = {
+ .mode_fixup = dw_mipi_dsi_encoder_mode_fixup,
+ .commit = dw_mipi_dsi_encoder_commit,
+ .mode_set = dw_mipi_dsi_encoder_mode_set,
+ .disable = dw_mipi_dsi_encoder_disable,
+};
+
+static struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static int dw_mipi_dsi_connector_get_modes(struct drm_connector *connector)
+{
+ struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+ return drm_panel_get_modes(dsi->panel);
+}
+
+static enum drm_mode_status dw_mipi_dsi_mode_valid(
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+ enum drm_mode_status mode_status = MODE_OK;
+
+ if (dsi->pdata->mode_valid)
+ mode_status = dsi->pdata->mode_valid(connector, mode);
+
+ return mode_status;
+}
+
+static struct drm_encoder *dw_mipi_dsi_connector_best_encoder(
+ struct drm_connector *connector)
+{
+ struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+ return &dsi->encoder;
+}
+
+static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
+ .get_modes = dw_mipi_dsi_connector_get_modes,
+ .mode_valid = dw_mipi_dsi_mode_valid,
+ .best_encoder = dw_mipi_dsi_connector_best_encoder,
+};
+
+static enum drm_connector_status
+dw_mipi_dsi_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = dw_mipi_dsi_detect,
+ .destroy = dw_mipi_dsi_drm_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int dw_mipi_dsi_register(struct drm_device *drm,
+ struct dw_mipi_dsi *dsi)
+{
+ struct drm_encoder *encoder = &dsi->encoder;
+ struct drm_connector *connector = &dsi->connector;
+ struct device *dev = dsi->dev;
+ int ret;
+
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
+ dev->of_node);
+ /*
+ * If we failed to find the CRTC(s) which this encoder is
+ * supposed to be connected to, it's because the CRTC has
+ * not been registered yet. Defer probing, and hope that
+ * the required CRTC is added later.
+ */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ drm_encoder_helper_add(&dsi->encoder,
+ &dw_mipi_dsi_encoder_helper_funcs);
+ ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
+ DRM_MODE_ENCODER_DSI, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to initialize encoder with drm\n");
+ return ret;
+ }
+
+ drm_connector_helper_add(connector,
+ &dw_mipi_dsi_connector_helper_funcs);
+
+ drm_connector_init(drm, &dsi->connector,
+ &dw_mipi_dsi_atomic_connector_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+}
+
+static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi)
+{
+ struct device_node *np = dsi->dev->of_node;
+
+ dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(dsi->grf_regmap)) {
+ dev_err(dsi->dev, "Unable to get rockchip,grf\n");
+ return PTR_ERR(dsi->grf_regmap);
+ }
+
+ return 0;
+}
+
+static enum drm_mode_status rk3288_mipi_dsi_mode_valid(
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ /*
+ * The VID_PKT_SIZE field in the DSI_VID_PKT_CFG
+ * register is 11-bit.
+ */
+ if (mode->hdisplay > 0x7ff)
+ return MODE_BAD_HVALUE;
+
+ /*
+ * The V_ACTIVE_LINES field in the DSI_VTIMING_CFG
+ * register is 11-bit.
+ */
+ if (mode->vdisplay > 0x7ff)
+ return MODE_BAD_VVALUE;
+
+ return MODE_OK;
+}
+
+static struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_drv_data = {
+ .max_data_lanes = 4,
+ .mode_valid = rk3288_mipi_dsi_mode_valid,
+};
+
+static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
+ {
+ .compatible = "rockchip,rk3288-mipi-dsi",
+ .data = &rk3288_mipi_dsi_drv_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_ids);
+
+static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ const struct of_device_id *of_id =
+ of_match_device(dw_mipi_dsi_dt_ids, dev);
+ const struct dw_mipi_dsi_plat_data *pdata = of_id->data;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = data;
+ struct dw_mipi_dsi *dsi;
+ struct resource *res;
+ int ret;
+
+ dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+ if (!dsi)
+ return -ENOMEM;
+
+ dsi->dev = dev;
+ dsi->pdata = pdata;
+
+ ret = rockchip_mipi_parse_dt(dsi);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ dsi->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(dsi->base))
+ return PTR_ERR(dsi->base);
+
+ dsi->pllref_clk = devm_clk_get(dev, "ref");
+ if (IS_ERR(dsi->pllref_clk)) {
+ ret = PTR_ERR(dsi->pllref_clk);
+ dev_err(dev, "Unable to get pll reference clock: %d\n", ret);
+ return ret;
+ }
+
+ dsi->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(dsi->pclk)) {
+ ret = PTR_ERR(dsi->pclk);
+ dev_err(dev, "Unable to get pclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dsi->pllref_clk);
+ if (ret) {
+ dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__);
+ return ret;
+ }
+
+ ret = dw_mipi_dsi_register(drm, dsi);
+ if (ret) {
+ dev_err(dev, "Failed to register mipi_dsi: %d\n", ret);
+ goto err_pllref;
+ }
+
+ dev_set_drvdata(dev, dsi);
+
+ dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
+ dsi->dsi_host.dev = dev;
+ return mipi_dsi_host_register(&dsi->dsi_host);
+
+err_pllref:
+ clk_disable_unprepare(dsi->pllref_clk);
+ return ret;
+}
+
+static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+ mipi_dsi_host_unregister(&dsi->dsi_host);
+ clk_disable_unprepare(dsi->pllref_clk);
+}
+
+static const struct component_ops dw_mipi_dsi_ops = {
+ .bind = dw_mipi_dsi_bind,
+ .unbind = dw_mipi_dsi_unbind,
+};
+
+static int dw_mipi_dsi_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &dw_mipi_dsi_ops);
+}
+
+static int dw_mipi_dsi_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dw_mipi_dsi_ops);
+ return 0;
+}
+
+static struct platform_driver dw_mipi_dsi_driver = {
+ .probe = dw_mipi_dsi_probe,
+ .remove = dw_mipi_dsi_remove,
+ .driver = {
+ .of_match_table = dw_mipi_dsi_dt_ids,
+ .name = DRIVER_NAME,
+ },
+};
+module_platform_driver(dw_mipi_dsi_driver);
+
+MODULE_DESCRIPTION("ROCKCHIP MIPI DSI host controller driver");
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index f5b3da2f92d7..46c2a8dfd8aa 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -818,6 +818,9 @@ int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc,
case DRM_MODE_CONNECTOR_HDMIA:
VOP_CTRL_SET(vop, hdmi_en, 1);
break;
+ case DRM_MODE_CONNECTOR_DSI:
+ VOP_CTRL_SET(vop, mipi_en, 1);
+ break;
default:
DRM_ERROR("unsupport connector_type[%d]\n", connector_type);
return -EINVAL;
@@ -878,9 +881,14 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
+ struct vop *vop = to_vop(crtc);
+
if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
return false;
+ adjusted_mode->clock =
+ clk_round_rate(vop->dclk, mode->clock * 1000) / 1000;
+
return true;
}
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 745e996d2dbc..4cbf26555093 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -176,7 +176,7 @@ void ttm_bo_add_to_lru(struct ttm_buffer_object *bo)
list_add_tail(&bo->lru, &man->lru);
kref_get(&bo->list_kref);
- if (bo->ttm != NULL) {
+ if (bo->ttm && !(bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) {
list_add_tail(&bo->swap, &bo->glob->swap_lru);
kref_get(&bo->list_kref);
}
@@ -228,6 +228,27 @@ void ttm_bo_del_sub_from_lru(struct ttm_buffer_object *bo)
}
EXPORT_SYMBOL(ttm_bo_del_sub_from_lru);
+void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo)
+{
+ struct ttm_bo_device *bdev = bo->bdev;
+ struct ttm_mem_type_manager *man;
+
+ lockdep_assert_held(&bo->resv->lock.base);
+
+ if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) {
+ list_del_init(&bo->swap);
+ list_del_init(&bo->lru);
+
+ } else {
+ if (bo->ttm && !(bo->ttm->page_flags & TTM_PAGE_FLAG_SG))
+ list_move_tail(&bo->swap, &bo->glob->swap_lru);
+
+ man = &bdev->man[bo->mem.mem_type];
+ list_move_tail(&bo->lru, &man->lru);
+ }
+}
+EXPORT_SYMBOL(ttm_bo_move_to_lru_tail);
+
/*
* Call bo->mutex locked.
*/
@@ -1170,9 +1191,15 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
if (likely(!ret))
ret = ttm_bo_validate(bo, placement, interruptible, false);
- if (!resv)
+ if (!resv) {
ttm_bo_unreserve(bo);
+ } else if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
+ spin_lock(&bo->glob->lru_lock);
+ ttm_bo_add_to_lru(bo);
+ spin_unlock(&bo->glob->lru_lock);
+ }
+
if (unlikely(ret))
ttm_bo_unref(&bo);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c
index 6377e8151000..67cebb23c940 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c
@@ -247,7 +247,7 @@ static void __vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header)
{
struct vmw_cmdbuf_man *man = header->man;
- BUG_ON(!spin_is_locked(&man->lock));
+ lockdep_assert_held_once(&man->lock);
if (header->inline_space) {
vmw_cmdbuf_header_inline_free(header);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 274c90da44a7..b221a8c40282 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -763,21 +763,25 @@ static int vmw_create_dmabuf_proxy(struct drm_device *dev,
uint32_t format;
struct drm_vmw_size content_base_size;
struct vmw_resource *res;
+ unsigned int bytes_pp;
int ret;
switch (mode_cmd->depth) {
case 32:
case 24:
format = SVGA3D_X8R8G8B8;
+ bytes_pp = 4;
break;
case 16:
case 15:
format = SVGA3D_R5G6B5;
+ bytes_pp = 2;
break;
case 8:
format = SVGA3D_P8;
+ bytes_pp = 1;
break;
default:
@@ -785,7 +789,7 @@ static int vmw_create_dmabuf_proxy(struct drm_device *dev,
return -EINVAL;
}
- content_base_size.width = mode_cmd->width;
+ content_base_size.width = mode_cmd->pitch / bytes_pp;
content_base_size.height = mode_cmd->height;
content_base_size.depth = 1;