Merge "soc: qcom: smem: Update max processors in soc"
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
index ffd4345..a00984f 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
@@ -46,6 +46,17 @@
the first cell will be used to define gpio number and the
second denotes the flags for this gpio
+- qcom,gpios-disallowed:
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: Array of the GPIO hardware numbers corresponding to GPIOs
+ which the APSS processor is not allowed to configure.
+ The hardware numbers are indexed from 1.
+ The interrupt resources for these GPIOs must not be defined
+ in "interrupts" and "interrupt-names" properties.
+ GPIOs defined in this array won't be registered as pins
+ in the pinctrl device or gpios in the gpio chip.
+
Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
a general description of GPIO and interrupt bindings.
@@ -234,6 +245,7 @@
gpio-controller;
#gpio-cells = <2>;
+ qcom,gpios-disallowed = <1 20>;
pm8921_gpio_keys: gpio-keys {
volume-keys {
diff --git a/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt b/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
index 290ec06..7307aed 100644
--- a/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
+++ b/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
@@ -15,6 +15,21 @@
- io-channels: Should contain IIO channel specifier for the ADC channel,
which report chip die temperature.
- io-channel-names: Should contain "thermal".
+- qcom,temperature-threshold-set: Defines the temperature threshold set to
+ configure. Supported values are 0 to 3. Each set defines
+ the over-temperature stage 1, 2, and 3 temperature
+ thresholds. If this property is not specified, then set 0
+ will be used by default.
+ Threshold set mapping (TEMP_GEN1, TEMP_GEN2 rev 0):
+ 0 = {105 C, 125 C, 145 C}
+ 1 = {110 C, 130 C, 150 C}
+ 2 = {115 C, 135 C, 155 C}
+ 3 = {120 C, 140 C, 160 C}
+ Threshold set mapping (TEMP_GEN2 rev 1 and above):
+ 0 = { 90 C, 110 C, 140 C}
+ 1 = { 95 C, 115 C, 145 C}
+ 2 = {100 C, 120 C, 150 C}
+ 3 = {105 C, 125 C, 155 C}
Example:
diff --git a/arch/arm64/boot/dts/qcom/kona.dtsi b/arch/arm64/boot/dts/qcom/kona.dtsi
index bf23646..0e35a4d 100644
--- a/arch/arm64/boot/dts/qcom/kona.dtsi
+++ b/arch/arm64/boot/dts/qcom/kona.dtsi
@@ -1361,6 +1361,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "cdsprpc-smd";
iommus = <&apps_smmu 0x1001 0x0460>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1368,6 +1370,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "cdsprpc-smd";
iommus = <&apps_smmu 0x1002 0x0460>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1375,6 +1379,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "cdsprpc-smd";
iommus = <&apps_smmu 0x1003 0x0460>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1382,6 +1388,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "cdsprpc-smd";
iommus = <&apps_smmu 0x1004 0x0460>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1389,6 +1397,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "cdsprpc-smd";
iommus = <&apps_smmu 0x1005 0x0460>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1396,6 +1406,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "cdsprpc-smd";
iommus = <&apps_smmu 0x1006 0x0460>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1403,6 +1415,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "cdsprpc-smd";
iommus = <&apps_smmu 0x1007 0x0460>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1410,6 +1424,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "cdsprpc-smd";
iommus = <&apps_smmu 0x1008 0x0460>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1418,6 +1434,9 @@
label = "cdsprpc-smd";
qcom,secure-context-bank;
iommus = <&apps_smmu 0x1009 0x0460>;
+ dma-ranges = <0x60000000 0x60000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
+ qcom,iommu-vmid = <0xA>; /* VMID_CP_PIXEL */
dma-coherent;
};
@@ -1425,6 +1444,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&apps_smmu 0x1803 0x0>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1432,6 +1453,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&apps_smmu 0x1804 0x0>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1439,6 +1462,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&apps_smmu 0x1805 0x0>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1446,6 +1471,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "sdsprpc-smd";
iommus = <&apps_smmu 0x0541 0x0>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1453,6 +1480,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "sdsprpc-smd";
iommus = <&apps_smmu 0x0542 0x0>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
dma-coherent;
};
@@ -1460,6 +1489,8 @@
compatible = "qcom,msm-fastrpc-compute-cb";
label = "sdsprpc-smd";
iommus = <&apps_smmu 0x0543 0x0>;
+ dma-ranges = <0x80000000 0x80000000 0x78000000>;
+ qcom,iommu-faults = "stall-disable";
shared-cb = <4>;
dma-coherent;
};
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 215ae14..71f40c0 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -226,7 +226,6 @@ struct fastrpc_ctx_lst {
struct fastrpc_smmu {
struct device *dev;
- struct dma_iommu_mapping *mapping;
const char *dev_name;
int cb;
int enabled;
@@ -505,7 +504,7 @@ static void fastrpc_buf_free(struct fastrpc_buf *buf, int cache)
int destVM[1] = {VMID_HLOS};
int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
- if (fl->sctx->smmu.cb && fl->cid != SDSP_DOMAIN_ID)
+ if (fl->sctx->smmu.cb)
buf->phys &= ~((uint64_t)fl->sctx->smmu.cb << 32);
vmid = fl->apps->channel[fl->cid].vmid;
if (vmid) {
@@ -872,8 +871,7 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd,
map->phys = sg_dma_address(map->table->sgl);
if (sess->smmu.cb) {
- if (fl->cid != SDSP_DOMAIN_ID)
- map->phys += ((uint64_t)sess->smmu.cb << 32);
+ map->phys += ((uint64_t)sess->smmu.cb << 32);
for_each_sg(map->table->sgl, sgl, map->table->nents,
sgl_index)
map->size += sg_dma_len(sgl);
@@ -975,7 +973,7 @@ static int fastrpc_buf_alloc(struct fastrpc_file *fl, size_t size,
current->comm, __func__, size, (int)buf->virt);
goto bail;
}
- if (fl->sctx->smmu.cb && fl->cid != SDSP_DOMAIN_ID)
+ if (fl->sctx->smmu.cb)
buf->phys += ((uint64_t)fl->sctx->smmu.cb << 32);
vmid = fl->apps->channel[fl->cid].vmid;
if (vmid) {
@@ -3509,10 +3507,8 @@ static int fastrpc_cb_probe(struct device *dev)
struct fastrpc_session_ctx *sess;
struct of_phandle_args iommuspec;
const char *name;
- dma_addr_t start = 0x80000000;
int err = 0;
unsigned int sharedcb_count = 0, cid, i, j;
- int secure_vmid = VMID_CP_PIXEL, cache_flush = 1;
VERIFY(err, NULL != (name = of_get_property(dev->of_node,
"label", NULL)));
@@ -3547,60 +3543,15 @@ static int fastrpc_cb_probe(struct device *dev)
"dma-coherent");
sess->smmu.secure = of_property_read_bool(dev->of_node,
"qcom,secure-context-bank");
-
- /* Software workaround for SMMU interconnect HW bug */
- if (cid == SDSP_DOMAIN_ID) {
- sess->smmu.cb = iommuspec.args[0] & 0x3;
- VERIFY(err, sess->smmu.cb);
- if (err)
- goto bail;
- start += ((uint64_t)sess->smmu.cb << 32);
- dma_set_mask(dev, DMA_BIT_MASK(34));
- } else {
- sess->smmu.cb = iommuspec.args[0] & 0xf;
- }
-
- if (sess->smmu.secure)
- start = 0x60000000;
- VERIFY(err, !IS_ERR_OR_NULL(sess->smmu.mapping =
- arm_iommu_create_mapping(&platform_bus_type,
- start, 0x78000000)));
- if (err) {
- pr_err("adsprpc: %s: creating iommu mapping failed for %s, ret %pK\n",
- __func__, dev_name(dev), sess->smmu.mapping);
- goto bail;
- }
-
- err = iommu_domain_set_attr(sess->smmu.mapping->domain,
- DOMAIN_ATTR_CB_STALL_DISABLE, &cache_flush);
- if (err) {
- pr_err("adsprpc: %s: setting CB stall iommu attribute failed for %s with err %d\n",
- __func__, dev_name(dev), err);
- goto bail;
- }
- if (sess->smmu.secure) {
- err = iommu_domain_set_attr(sess->smmu.mapping->domain,
- DOMAIN_ATTR_SECURE_VMID, &secure_vmid);
- if (err) {
- pr_err("adsprpc: %s: setting secure iommu attribute failed for %s with err %d\n",
- __func__, dev_name(dev), err);
- goto bail;
- }
- }
-
- err = arm_iommu_attach_device(dev, sess->smmu.mapping);
- if (err) {
- pr_err("adsprpc: %s: attaching iommu device failed for %s with err %d\n",
- __func__, dev_name(dev), err);
- goto bail;
- }
-
+ sess->smmu.cb = iommuspec.args[0] & 0xf;
sess->smmu.dev = dev;
sess->smmu.dev_name = dev_name(dev);
sess->smmu.enabled = 1;
+
if (!sess->smmu.dev->dma_parms)
sess->smmu.dev->dma_parms = devm_kzalloc(sess->smmu.dev,
sizeof(*sess->smmu.dev->dma_parms), GFP_KERNEL);
+
dma_set_max_seg_size(sess->smmu.dev, DMA_BIT_MASK(32));
dma_set_seg_boundary(sess->smmu.dev, (unsigned long)DMA_BIT_MASK(64));
@@ -3824,14 +3775,8 @@ static void fastrpc_deinit(void)
for (j = 0; j < NUM_SESSIONS; j++) {
struct fastrpc_session_ctx *sess = &chan->session[j];
- if (sess->smmu.dev) {
- arm_iommu_detach_device(sess->smmu.dev);
+ if (sess->smmu.dev)
sess->smmu.dev = NULL;
- }
- if (sess->smmu.mapping) {
- arm_iommu_release_mapping(sess->smmu.mapping);
- sess->smmu.mapping = NULL;
- }
}
kfree(chan->rhvm.vmid);
kfree(chan->rhvm.vmperm);
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
index a30fab2..d138aa5 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.c
@@ -12,6 +12,8 @@
#include <linux/clk.h>
#include <linux/bitmap.h>
#include <linux/sde_rsc.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/llcc-qcom.h>
#include "msm_prop.h"
@@ -297,6 +299,107 @@ static inline enum sde_crtc_client_type _get_sde_client_type(
return RT_CLIENT;
}
+/**
+ * @_sde_core_perf_activate_llcc() - Activates/deactivates the system llcc
+ * @kms - pointer to the kms
+ * @uid - ID for which the llcc would be activated
+ * @activate - boolean to indicate if activate/deactivate the LLCC
+ *
+ * Function assumes that caller has already acquired the "sde_core_perf_lock",
+ * which would protect from any race condition between CRTC's
+ */
+static int _sde_core_perf_activate_llcc(struct sde_kms *kms,
+ u32 uid, bool activate)
+{
+ struct llcc_slice_desc *slice;
+ struct drm_device *drm_dev;
+ struct device *dev;
+ struct platform_device *pdev;
+ int rc = 0;
+
+ if (!kms || !kms->dev || !kms->dev->dev) {
+ SDE_ERROR("wrong params won't activate llcc\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ drm_dev = kms->dev;
+ dev = drm_dev->dev;
+ pdev = to_platform_device(dev);
+
+ /* If LLCC is already in the requested state, skip */
+ SDE_EVT32(activate, kms->perf.llcc_active);
+ if ((activate && kms->perf.llcc_active) ||
+ (!activate && !kms->perf.llcc_active)) {
+ SDE_DEBUG("skip llcc request:%d state:%d\n",
+ activate, kms->perf.llcc_active);
+ goto exit;
+ }
+
+ SDE_DEBUG("activate/deactivate the llcc request:%d state:%d\n",
+ activate, kms->perf.llcc_active);
+
+ slice = llcc_slice_getd(uid);
+ if (IS_ERR_OR_NULL(slice)) {
+ SDE_ERROR("failed to get llcc slice for uid:%d\n", uid);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (activate) {
+ llcc_slice_activate(slice);
+ kms->perf.llcc_active = true;
+ } else {
+ llcc_slice_deactivate(slice);
+ kms->perf.llcc_active = false;
+ }
+
+exit:
+ if (rc)
+ SDE_ERROR("error activating llcc:%d rc:%d\n",
+ activate, rc);
+ return rc;
+
+}
+
+static void _sde_core_perf_crtc_update_llcc(struct sde_kms *kms,
+ struct drm_crtc *crtc)
+{
+ struct drm_crtc *tmp_crtc;
+ struct sde_crtc *sde_crtc;
+ enum sde_crtc_client_type curr_client_type
+ = sde_crtc_get_client_type(crtc);
+ u32 total_llcc_active = 0;
+
+ if (!kms->perf.catalog->sc_cfg.has_sys_cache) {
+ SDE_DEBUG("System Cache is not enabled!. Won't use\n");
+ return;
+ }
+
+ drm_for_each_crtc(tmp_crtc, crtc->dev) {
+ if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
+ _is_crtc_client_type_matches(tmp_crtc, curr_client_type,
+ &kms->perf)) {
+
+ /* use current perf, which are the values voted */
+ sde_crtc = to_sde_crtc(tmp_crtc);
+ total_llcc_active |=
+ sde_crtc->cur_perf.llcc_active;
+
+ SDE_DEBUG("crtc=%d llcc:%llu active:0x%x\n",
+ tmp_crtc->base.id,
+ sde_crtc->cur_perf.llcc_active,
+ total_llcc_active);
+
+ if (total_llcc_active)
+ break;
+ }
+ }
+
+ _sde_core_perf_activate_llcc(kms, LLCC_ROTATOR,
+ total_llcc_active ? true : false);
+}
+
static void _sde_core_perf_crtc_update_bus(struct sde_kms *kms,
struct drm_crtc *crtc, u32 bus_id)
{
@@ -494,7 +597,7 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
int params_changed, bool stop_req)
{
struct sde_core_perf_params *new, *old;
- int update_bus = 0, update_clk = 0;
+ int update_bus = 0, update_clk = 0, update_llcc = 0;
u64 clk_rate = 0;
struct sde_crtc *sde_crtc;
struct sde_crtc_state *sde_cstate;
@@ -534,6 +637,28 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
new = &sde_crtc->new_perf;
if (_sde_core_perf_crtc_is_power_on(crtc) && !stop_req) {
+
+ /*
+ * cases for the llcc update.
+ * 1. llcc is transitioning: 'inactive->active' during kickoff,
+ * for current request.
+ * 2. llcc is transitioning: 'active->inactive'at the end of the
+ * commit or during stop
+ */
+
+ if ((params_changed &&
+ new->llcc_active && !old->llcc_active) ||
+ (!params_changed &&
+ !new->llcc_active && old->llcc_active)) {
+
+ SDE_DEBUG("crtc=%d p=%d new_llcc=%d, old_llcc=%d\n",
+ crtc->base.id, params_changed,
+ new->llcc_active, old->llcc_active);
+
+ old->llcc_active = new->llcc_active;
+ update_llcc = 1;
+ }
+
for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
/*
* cases for bus bandwidth update.
@@ -604,11 +729,12 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
update_clk = 1;
}
} else {
- SDE_DEBUG("crtc=%d disable\n", crtc->base.id);
+ SDE_ERROR("crtc=%d disable\n", crtc->base.id);
memset(old, 0, sizeof(*old));
memset(new, 0, sizeof(*new));
update_bus = ~0;
update_clk = 1;
+ update_llcc = 1;
}
trace_sde_perf_crtc_update(crtc->base.id,
new->bw_ctl[SDE_POWER_HANDLE_DBUS_ID_MNOC],
@@ -620,6 +746,9 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
new->core_clk_rate, stop_req,
update_bus, update_clk, params_changed);
+ if (update_llcc)
+ _sde_core_perf_crtc_update_llcc(kms, crtc);
+
for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
if (update_bus & BIT(i))
_sde_core_perf_crtc_update_bus(kms, crtc, i);
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.h b/drivers/gpu/drm/msm/sde/sde_core_perf.h
index 45c4bc6..2d0de09 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.h
@@ -21,11 +21,13 @@
* @max_per_pipe_ib: maximum instantaneous bandwidth request
* @bw_ctl: arbitrated bandwidth request
* @core_clk_rate: core clock rate request
+ * @llcc_active: request to activate/deactivate the llcc
*/
struct sde_core_perf_params {
u64 max_per_pipe_ib[SDE_POWER_HANDLE_DBUS_ID_MAX];
u64 bw_ctl[SDE_POWER_HANDLE_DBUS_ID_MAX];
u64 core_clk_rate;
+ bool llcc_active;
};
/**
@@ -59,6 +61,7 @@ struct sde_core_perf_tune {
* @bw_vote_mode: apps rsc vs display rsc bandwidth vote mode
* @sde_rsc_available: is display rsc available
* @bw_vote_mode_updated: bandwidth vote mode update
+ * @llcc_active: status of the llcc, true if active.
*/
struct sde_core_perf {
struct drm_device *dev;
@@ -78,6 +81,7 @@ struct sde_core_perf {
u32 bw_vote_mode;
bool sde_rsc_available;
bool bw_vote_mode_updated;
+ bool llcc_active;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 2bcadbe..d7d0974 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -1234,7 +1234,8 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
state->src_x >> 16, state->src_y >> 16,
state->src_w >> 16, state->src_h >> 16,
state->crtc_x, state->crtc_y,
- state->crtc_w, state->crtc_h);
+ state->crtc_w, state->crtc_h,
+ pstate->rotation);
stage_idx = zpos_cnt[pstate->stage]++;
stage_cfg->stage[pstate->stage][stage_idx] =
@@ -2965,10 +2966,18 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
* required. However, if those planes were power collapsed since
* last commit cycle, driver has to restore the hardware state
* of those planes explicitly here prior to plane flush.
+ * Also use this iteration to see if any plane requires cache,
+ * so during the perf update driver can activate/deactivate
+ * the cache accordingly.
*/
- drm_atomic_crtc_for_each_plane(plane, crtc)
+ sde_crtc->new_perf.llcc_active = false;
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
sde_plane_restore(plane);
+ if (sde_plane_is_cache_required(plane))
+ sde_crtc->new_perf.llcc_active = true;
+ }
+
/* wait for acquire fences before anything else is done */
_sde_crtc_wait_for_fences(crtc);
@@ -5737,9 +5746,17 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
sde_cp_crtc_init(crtc);
sde_cp_crtc_install_properties(crtc);
+ sde_crtc->cur_perf.llcc_active = false;
+ sde_crtc->new_perf.llcc_active = false;
+
kthread_init_delayed_work(&sde_crtc->idle_notify_work,
__sde_crtc_idle_notify_work);
+ SDE_DEBUG("crtc=%d new_llcc=%d, old_llcc=%d\n",
+ crtc->base.id,
+ sde_crtc->new_perf.llcc_active,
+ sde_crtc->cur_perf.llcc_active);
+
SDE_DEBUG("%s: successfully initialized crtc\n", sde_crtc->name);
return crtc;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index 1f9a4d0..497cc40 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -642,6 +642,16 @@ static const struct sde_format sde_format_map_tp10_ubwc[] = {
SDE_FETCH_UBWC, 4, SDE_TILE_HEIGHT_NV12),
};
+inline bool sde_format_is_tp10_ubwc(const struct sde_format *fmt)
+{
+ if (SDE_FORMAT_IS_YUV(fmt) && SDE_FORMAT_IS_DX(fmt) &&
+ SDE_FORMAT_IS_UBWC(fmt) &&
+ (fmt->num_planes == 4) && fmt->unpack_tight)
+ return true;
+ else
+ return false;
+}
+
/* _sde_get_v_h_subsample_rate - Get subsample rates for all formats we support
* Note: Not using the drm_format_*_subsampling since we have formats
*/
@@ -1334,3 +1344,38 @@ uint32_t sde_populate_formats(
return i;
}
+
+int sde_format_validate_fmt(struct msm_kms *kms,
+ const struct msm_format *msm_fmt,
+ const struct sde_format_extended *fmt_list)
+{
+ const struct msm_format *fmt_tmp;
+ bool valid_format = false;
+ int ret = 0;
+
+ if (!msm_fmt || !fmt_list) {
+ SDE_ERROR("invalid fmt:%d list:%d\n",
+ !msm_fmt, !fmt_list);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ while (fmt_list->fourcc_format) {
+ fmt_tmp = sde_get_msm_format(kms,
+ fmt_list->fourcc_format,
+ fmt_list->modifier);
+ if (fmt_tmp &&
+ (fmt_tmp->pixel_format == msm_fmt->pixel_format)) {
+ valid_format = true;
+ break;
+ }
+ ++fmt_list;
+ }
+
+ if (!valid_format) {
+ SDE_ERROR("fmt:%d not found within the list!\n", *msm_fmt);
+ ret = -EINVAL;
+ }
+exit:
+ return ret;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.h b/drivers/gpu/drm/msm/sde/sde_formats.h
index 0e55df4..32c676c 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.h
+++ b/drivers/gpu/drm/msm/sde/sde_formats.h
@@ -142,4 +142,25 @@ uint32_t sde_format_get_framebuffer_size(
const uint32_t *pitches,
const uint64_t modifier);
+/**
+ * sde_format_is_tp10_ubwc - check if the format is tp10 ubwc
+ * @format: DRM pixel format
+ *
+ * Return: returns true if the format is tp10 ubwc, otherwise false.
+ */
+inline bool sde_format_is_tp10_ubwc(const struct sde_format *fmt);
+
+/**
+ * sde_format_validate_fmt - validates if the format "msm_fmt" is within
+ * the list "fmt_list"
+ * @kms: pointer to the kms object
+ * @msm_fmt: pointer to the format to look within the list
+ * @fmt_list: list where driver will loop to look for the 'msm_fmt' format.
+ * @result: returns 0 if the format is found, otherwise will return an
+ * error code.
+ */
+int sde_format_validate_fmt(struct msm_kms *kms,
+ const struct msm_format *msm_fmt,
+ const struct sde_format_extended *fmt_list);
+
#endif /*_SDE_FORMATS_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index d5600f5..f345710 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -64,6 +64,10 @@
#define MAX_DOWNSCALE_RATIO 4
#define SSPP_UNITY_SCALE 1
+#define MAX_DOWNSCALE_RATIO_INLINE_ROT_RT_DEFAULT 2
+#define MAX_DOWNSCALE_RATIO_INLINE_ROT_NRT_DEFAULT 4
+#define MAX_PRE_ROT_HEIGHT_INLINE_ROT_DEFAULT 1088
+
#define MAX_HORZ_DECIMATION 4
#define MAX_VERT_DECIMATION 4
@@ -1098,6 +1102,23 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
sblk->format_list = sde_cfg->vig_formats;
sblk->virt_format_list = sde_cfg->virt_vig_formats;
+ if (IS_SDE_INLINE_ROT_REV_100(sde_cfg->true_inline_rot_rev)) {
+ set_bit(SDE_SSPP_TRUE_INLINE_ROT_V1, &sspp->features);
+ sblk->in_rot_format_list = sde_cfg->inline_rot_formats;
+ sblk->in_rot_maxdwnscale_rt =
+ sde_cfg->true_inline_dwnscale_rt;
+ sblk->in_rot_maxdwnscale_nrt =
+ sde_cfg->true_inline_dwnscale_nrt;
+ sblk->in_rot_maxheight =
+ MAX_PRE_ROT_HEIGHT_INLINE_ROT_DEFAULT;
+ }
+
+ if (sde_cfg->sc_cfg.has_sys_cache) {
+ set_bit(SDE_PERF_SSPP_SYS_CACHE, &sspp->perf_features);
+ sblk->llcc_scid = sde_cfg->sc_cfg.llcc_scid;
+ sblk->llcc_slice_size =
+ sde_cfg->sc_cfg.llcc_slice_size;
+ }
}
static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg,
@@ -2018,6 +2039,63 @@ static void _sde_dspp_setup_blocks(struct sde_mdss_cfg *sde_cfg,
}
}
+static int sde_rot_parse_dt(struct device_node *np,
+ struct sde_mdss_cfg *sde_cfg)
+{
+ struct platform_device *pdev;
+ struct of_phandle_args phargs;
+ struct llcc_slice_desc *slice;
+ int rc = 0;
+
+ rc = of_parse_phandle_with_args(np,
+ "qcom,sde-inline-rotator", "#list-cells",
+ 0, &phargs);
+
+ if (rc) {
+ /*
+ * This is not a fatal error, system cache can be disabled
+ * in device tree, anyways recommendation is to have it
+ * enabled, so print an error but don't fail
+ */
+ SDE_DEBUG("sys cache will be disabled rc:%d\n", rc);
+ rc = 0;
+ goto exit;
+ }
+
+ if (!phargs.np || !phargs.args_count) {
+ SDE_ERROR("wrong phandle args %d %d\n",
+ !phargs.np, !phargs.args_count);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ pdev = of_find_device_by_node(phargs.np);
+ if (!pdev) {
+ SDE_ERROR("invalid sde rotator node\n");
+ goto exit;
+ }
+
+ slice = llcc_slice_getd(LLCC_ROTATOR);
+ if (IS_ERR_OR_NULL(slice)) {
+ SDE_ERROR("failed to get rotator slice!\n");
+ rc = -EINVAL;
+ goto cleanup;
+ }
+
+ sde_cfg->sc_cfg.llcc_scid = llcc_get_slice_id(slice);
+ sde_cfg->sc_cfg.llcc_slice_size = llcc_get_slice_size(slice);
+ llcc_slice_putd(slice);
+
+ sde_cfg->sc_cfg.has_sys_cache = true;
+
+ SDE_DEBUG("rotator llcc scid:%d slice_size:%zukb\n",
+ sde_cfg->sc_cfg.llcc_scid, sde_cfg->sc_cfg.llcc_slice_size);
+cleanup:
+ of_node_put(phargs.np);
+exit:
+ return rc;
+}
+
static int sde_dspp_top_parse_dt(struct device_node *np,
struct sde_mdss_cfg *sde_cfg)
{
@@ -3321,7 +3399,7 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
{
int rc = 0;
uint32_t dma_list_size, vig_list_size, wb2_list_size;
- uint32_t virt_vig_list_size;
+ uint32_t virt_vig_list_size, in_rot_list_size = 0;
uint32_t cursor_list_size = 0;
uint32_t index = 0;
@@ -3346,6 +3424,8 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
virt_vig_list_size = ARRAY_SIZE(plane_formats);
wb2_list_size = ARRAY_SIZE(wb2_formats);
+ if (IS_SDE_INLINE_ROT_REV_100(sde_cfg->true_inline_rot_rev))
+ in_rot_list_size = ARRAY_SIZE(true_inline_rot_v1_fmts);
sde_cfg->dma_formats = kcalloc(dma_list_size,
sizeof(struct sde_format_extended), GFP_KERNEL);
@@ -3376,6 +3456,16 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
goto end;
}
+ if (in_rot_list_size) {
+ sde_cfg->inline_rot_formats = kcalloc(in_rot_list_size,
+ sizeof(struct sde_format_extended), GFP_KERNEL);
+ if (!sde_cfg->inline_rot_formats) {
+ SDE_ERROR("failed to alloc inline rot format list\n");
+ rc = -ENOMEM;
+ goto end;
+ }
+ }
+
index = sde_copy_formats(sde_cfg->dma_formats, dma_list_size,
0, plane_formats, ARRAY_SIZE(plane_formats));
@@ -3391,7 +3481,10 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
index = sde_copy_formats(sde_cfg->wb_formats, wb2_list_size,
0, wb2_formats, ARRAY_SIZE(wb2_formats));
-
+ if (in_rot_list_size)
+ index = sde_copy_formats(sde_cfg->inline_rot_formats,
+ in_rot_list_size, 0, true_inline_rot_v1_fmts,
+ ARRAY_SIZE(true_inline_rot_v1_fmts));
end:
return rc;
}
@@ -3507,6 +3600,11 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
clear_bit(MDSS_INTR_AD4_1_INTR, sde_cfg->mdss_irqs);
sde_cfg->has_hdr = true;
sde_cfg->has_vig_p010 = true;
+ sde_cfg->true_inline_rot_rev = SDE_INLINE_ROT_VERSION_1_0_0;
+ sde_cfg->true_inline_dwnscale_rt =
+ MAX_DOWNSCALE_RATIO_INLINE_ROT_RT_DEFAULT;
+ sde_cfg->true_inline_dwnscale_nrt =
+ MAX_DOWNSCALE_RATIO_INLINE_ROT_NRT_DEFAULT;
} else {
SDE_ERROR("unsupported chipset id:%X\n", hw_rev);
sde_cfg->perf.min_prefill_lines = 0xffff;
@@ -3618,6 +3716,7 @@ void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg)
kfree(sde_cfg->vig_formats);
kfree(sde_cfg->wb_formats);
kfree(sde_cfg->virt_vig_formats);
+ kfree(sde_cfg->inline_rot_formats);
kfree(sde_cfg);
}
@@ -3649,6 +3748,10 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev)
if (rc)
goto end;
+ rc = sde_rot_parse_dt(np, sde_cfg);
+ if (rc)
+ goto end;
+
rc = sde_ctl_parse_dt(np, sde_cfg);
if (rc)
goto end;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index aaae43d..185cbee 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -83,6 +83,13 @@
#define IS_SDE_CTL_REV_100(rev) \
((rev) == SDE_CTL_CFG_VERSION_1_0_0)
+/**
+ * True inline rotation supported versions
+ */
+#define SDE_INLINE_ROT_VERSION_1_0_0 0x100
+#define IS_SDE_INLINE_ROT_REV_100(rev) \
+ ((rev) == SDE_INLINE_ROT_VERSION_1_0_0)
+
#define SDE_HW_UBWC_VER(rev) \
SDE_HW_VER((((rev) >> 8) & 0xF), (((rev) >> 4) & 0xF), ((rev) & 0xF))
@@ -101,6 +108,15 @@ enum {
IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_UBWC_VER_30)
/**
+ * Supported SSPP system cache settings
+ */
+#define SSPP_SYS_CACHE_EN_FLAG BIT(0)
+#define SSPP_SYS_CACHE_SCID BIT(1)
+#define SSPP_SYS_CACHE_OP_MODE BIT(2)
+#define SSPP_SYS_CACHE_OP_TYPE BIT(3)
+#define SSPP_SYS_CACHE_NO_ALLOC BIT(4)
+
+/**
* SDE INTERRUPTS - maintains the possible hw irq's allowed by HW
* The order in this enum must match the order of the irqs defined
* by 'sde_irq_map'
@@ -171,6 +187,7 @@ enum {
* @SDE_SSPP_SEC_UI_ALLOWED Allows secure-ui layers
* @SDE_SSPP_BLOCK_SEC_UI Blocks secure-ui layers
* @SDE_SSPP_SCALER_QSEED3LITE Qseed3lite algorithm support
+ * @SDE_SSPP_TRUE_INLINE_ROT_V1, Support of SSPP true inline rotation v1
* @SDE_SSPP_MAX maximum value
*/
enum {
@@ -198,6 +215,7 @@ enum {
SDE_SSPP_SEC_UI_ALLOWED,
SDE_SSPP_BLOCK_SEC_UI,
SDE_SSPP_SCALER_QSEED3LITE,
+ SDE_SSPP_TRUE_INLINE_ROT_V1,
SDE_SSPP_MAX
};
@@ -209,6 +227,7 @@ enum {
* @SDE_PERF_SSPP_TS_PREFILL_REC1 Supports prefill with traffic shaper multirec
* @SDE_PERF_SSPP_CDP Supports client driven prefetch
* @SDE_PERF_SSPP_QOS_FL_NOCALC Avoid fill level calc for QoS/danger/safe
+ * @SDE_PERF_SSPP_SYS_CACHE, SSPP supports system cache
* @SDE_PERF_SSPP_MAX Maximum value
*/
enum {
@@ -218,6 +237,7 @@ enum {
SDE_PERF_SSPP_TS_PREFILL_REC1,
SDE_PERF_SSPP_CDP,
SDE_PERF_SSPP_QOS_FL_NOCALC,
+ SDE_PERF_SSPP_SYS_CACHE,
SDE_PERF_SSPP_MAX
};
@@ -535,6 +555,12 @@ struct sde_qos_lut_tbl {
* @dgm_csc_blk: DGM CSC blocks
* @format_list: Pointer to list of supported formats
* @virt_format_list: Pointer to list of supported formats for virtual planes
+ * @in_rot_format_list: Pointer to list of supported formats for inline rotation
+ * @in_rot_maxdwnscale_rt: max downscale ratio for inline rotation rt clients
+ * @in_rot_maxdwnscale_nrt: max downscale ratio for inline rotation nrt clients
+ * @in_rot_maxheight: max pre rotated height for inline rotation
+ * @llcc_scid: scid for the system cache
+ * @llcc_slice size: slice size of the system cache
*/
struct sde_sspp_sub_blks {
u32 maxlinewidth;
@@ -563,6 +589,12 @@ struct sde_sspp_sub_blks {
const struct sde_format_extended *format_list;
const struct sde_format_extended *virt_format_list;
+ const struct sde_format_extended *in_rot_format_list;
+ u32 in_rot_maxdwnscale_rt;
+ u32 in_rot_maxdwnscale_nrt;
+ u32 in_rot_maxheight;
+ int llcc_scid;
+ size_t llcc_slice_size;
};
/**
@@ -946,6 +978,18 @@ struct sde_perf_cdp_cfg {
};
/**
+ * struct sde_sc_cfg - define system cache configuration
+ * @has_sys_cache: true if system cache is enabled
+ * @llcc_scid: scid for the system cache
+ * @llcc_slice_size: slice size of the system cache
+ */
+struct sde_sc_cfg {
+ bool has_sys_cache;
+ int llcc_scid;
+ size_t llcc_slice_size;
+};
+
+/**
* struct sde_perf_cfg - performance control settings
* @max_bw_low low threshold of maximum bandwidth (kbps)
* @max_bw_high high threshold of maximum bandwidth (kbps)
@@ -1035,12 +1079,16 @@ struct sde_perf_cfg {
* @virt_vig_formats Supported formats for virtual vig pipe
* @vbif_qos_nlvl number of vbif QoS priority level
* @ts_prefill_rev prefill traffic shaper feature revision
+ * @true_inline_rot_rev inline rotator feature revision
+ * @true_inline_dwnscale_rt true inline rotator downscale ratio for rt
+ * @true_inline_dwnscale_nrt true inline rotator downscale ratio for nrt
* @macrotile_mode UBWC parameter for macro tile channel distribution
* @pipe_order_type indicate if it is required to specify pipe order
* @delay_prg_fetch_start indicates if throttling the fetch start is required
* @has_qsync Supports qsync feature
* @has_3d_merge_reset Supports 3D merge reset
* @has_decimation Supports decimation
+ * @sc_cfg: system cache configuration
* @sui_misr_supported indicate if secure-ui-misr is supported
* @sui_block_xin_mask mask of all the xin-clients to be blocked during
* secure-ui when secure-ui-misr feature is supported
@@ -1052,6 +1100,7 @@ struct sde_perf_cfg {
* @sui_supported_blendstage secure-ui supported blendstage
* @has_cursor indicates if hardware cursor is supported
* @has_vig_p010 indicates if vig pipe supports p010 format
+ * @inline_rot_formats formats supported by the inline rotator feature
* @mdss_irqs bitmap with the irqs supported by the target
*/
struct sde_mdss_cfg {
@@ -1082,6 +1131,9 @@ struct sde_mdss_cfg {
bool has_idle_pc;
u32 vbif_qos_nlvl;
u32 ts_prefill_rev;
+ u32 true_inline_rot_rev;
+ u32 true_inline_dwnscale_rt;
+ u32 true_inline_dwnscale_nrt;
u32 macrotile_mode;
u32 pipe_order_type;
bool delay_prg_fetch_start;
@@ -1089,6 +1141,8 @@ struct sde_mdss_cfg {
bool has_3d_merge_reset;
bool has_decimation;
+ struct sde_sc_cfg sc_cfg;
+
bool sui_misr_supported;
u32 sui_block_xin_mask;
@@ -1158,6 +1212,7 @@ struct sde_mdss_cfg {
struct sde_format_extended *vig_formats;
struct sde_format_extended *wb_formats;
struct sde_format_extended *virt_vig_formats;
+ struct sde_format_extended *inline_rot_formats;
DECLARE_BITMAP(mdss_irqs, MDSS_INTR_MAX);
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h
index 2b4a4de..2dba8e1 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h
@@ -149,3 +149,10 @@ static const struct sde_format_extended p010_ubwc_formats[] = {
{DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_DX |
DRM_FORMAT_MOD_QCOM_COMPRESSED},
};
+
+static const struct sde_format_extended true_inline_rot_v1_fmts[] = {
+ {DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+ {DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_COMPRESSED |
+ DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_TIGHT}, /* tp10 */
+ {0, 0},
+};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 5c1f0ff..717fc0b 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -919,6 +919,37 @@ static void sde_hw_sspp_setup_cdp(struct sde_hw_pipe *ctx,
SDE_REG_WRITE(&ctx->hw, cdp_cntl_offset, cdp_cntl);
}
+static void sde_hw_sspp_setup_sys_cache(struct sde_hw_pipe *ctx,
+ struct sde_hw_pipe_sc_cfg *cfg)
+{
+ u32 idx, val;
+
+ if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
+ return;
+
+ if (!cfg)
+ return;
+
+ val = SDE_REG_READ(&ctx->hw, SSPP_SYS_CACHE_MODE + idx);
+
+ if (cfg->flags & SSPP_SYS_CACHE_EN_FLAG)
+ val = (val & ~BIT(15)) | ((cfg->rd_en & 0x1) << 15);
+
+ if (cfg->flags & SSPP_SYS_CACHE_SCID)
+ val = (val & ~0x1F00) | ((cfg->rd_scid & 0x1f) << 8);
+
+ if (cfg->flags & SSPP_SYS_CACHE_OP_MODE)
+ val = (val & ~0xC0000) | ((cfg->op_mode & 0x3) << 18);
+
+ if (cfg->flags & SSPP_SYS_CACHE_OP_TYPE)
+ val = (val & ~0xF) | ((cfg->rd_op_type & 0xf) << 0);
+
+ if (cfg->flags & SSPP_SYS_CACHE_NO_ALLOC)
+ val = (val & ~0x10) | ((cfg->rd_noallocate & 0x1) << 4);
+
+ SDE_REG_WRITE(&ctx->hw, SSPP_SYS_CACHE_MODE + idx, val);
+}
+
static void _setup_layer_ops_colorproc(struct sde_hw_pipe *c,
unsigned long features)
{
@@ -1112,6 +1143,9 @@ static void _setup_layer_ops(struct sde_hw_pipe *c,
c->ops.setup_scaler = reg_dmav1_setup_vig_qseed3;
}
+ if (test_bit(SDE_PERF_SSPP_SYS_CACHE, &perf_features))
+ c->ops.setup_sys_cache = sde_hw_sspp_setup_sys_cache;
+
if (test_bit(SDE_PERF_SSPP_CDP, &perf_features))
c->ops.setup_cdp = sde_hw_sspp_setup_cdp;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index fcfd1d9..7f6ca34 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -248,6 +248,7 @@ enum {
* @rd_scid: system cache read block id
* @rd_noallocate: system cache read no allocate attribute
* @rd_op_type: system cache read operation type
+ * @flags: dirty flags to change the configuration
*/
struct sde_hw_pipe_sc_cfg {
u32 op_mode;
@@ -255,6 +256,7 @@ struct sde_hw_pipe_sc_cfg {
u32 rd_scid;
bool rd_noallocate;
u32 rd_op_type;
+ u32 flags;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 3f5e8fb..b94e552 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -204,27 +204,6 @@ static struct sde_hw_ctl *_sde_plane_get_hw_ctl(const struct drm_plane *plane)
return ctl;
}
-/**
- * _sde_plane_get_crtc_state - obtain crtc state attached to given plane state
- * @pstate: Pointer to drm plane state
- * return: Pointer to crtc state if success; pointer error, otherwise
- */
-static struct drm_crtc_state *_sde_plane_get_crtc_state(
- struct drm_plane_state *pstate)
-{
- struct drm_crtc_state *cstate;
-
- if (!pstate || !pstate->crtc)
- return NULL;
-
- if (pstate->state)
- cstate = drm_atomic_get_crtc_state(pstate->state, pstate->crtc);
- else
- cstate = pstate->crtc->state;
-
- return cstate;
-}
-
static bool sde_plane_enabled(const struct drm_plane_state *state)
{
return state && state->fb && state->crtc;
@@ -1013,6 +992,15 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde,
memset(scale_cfg, 0, sizeof(*scale_cfg));
memset(&pstate->pixel_ext, 0, sizeof(struct sde_hw_pixel_ext));
+ /*
+ * For inline rotation cases, scaler config is post-rotation,
+ * so swap the dimensions here. However, pixel extension will
+ * need pre-rotation settings, this will be corrected below
+ * when calculating pixel extension settings.
+ */
+ if (pstate->rotation & DRM_MODE_ROTATE_90)
+ swap(src_w, src_h);
+
decimated = DECIMATED_DIMENSION(src_w,
psde->pipe_cfg.horz_decimation);
scale_cfg->phase_step_x[SDE_SSPP_COMP_0] =
@@ -1049,10 +1037,19 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde,
}
scale_cfg->preload_x[i] = SDE_QSEED3_DEFAULT_PRELOAD_H;
scale_cfg->preload_y[i] = SDE_QSEED3_DEFAULT_PRELOAD_V;
- pstate->pixel_ext.num_ext_pxls_top[i] =
- scale_cfg->src_height[i];
- pstate->pixel_ext.num_ext_pxls_left[i] =
- scale_cfg->src_width[i];
+
+ /* For pixel extension we need the pre-rotated orientation */
+ if (pstate->rotation & DRM_MODE_ROTATE_90) {
+ pstate->pixel_ext.num_ext_pxls_top[i] =
+ scale_cfg->src_width[i];
+ pstate->pixel_ext.num_ext_pxls_left[i] =
+ scale_cfg->src_height[i];
+ } else {
+ pstate->pixel_ext.num_ext_pxls_top[i] =
+ scale_cfg->src_height[i];
+ pstate->pixel_ext.num_ext_pxls_left[i] =
+ scale_cfg->src_width[i];
+ }
}
if ((!(SDE_FORMAT_IS_YUV(fmt)) && (src_h == dst_h)
@@ -1581,18 +1578,20 @@ static int _sde_plane_color_fill(struct sde_plane *psde,
}
/**
- * sde_plane_rot_atomic_check - verify rotator update of the given state
- * @plane: Pointer to drm plane
- * @state: Pointer to drm plane state to be validated
- * return: 0 if success; error code otherwise
- */
+* sde_plane_rot_atomic_check - verify rotator update of the given state
+* @plane: Pointer to drm plane
+* @state: Pointer to drm plane state to be validated
+* return: 0 if success; error code otherwise
+*/
static int sde_plane_rot_atomic_check(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *state)
{
struct sde_plane *psde;
struct sde_plane_state *pstate, *old_pstate;
- struct drm_crtc_state *cstate;
int ret = 0;
+ const struct msm_format *msm_fmt;
+ const struct sde_format *fmt;
+ u32 height;
if (!plane || !state) {
SDE_ERROR("invalid plane/state\n");
@@ -1603,19 +1602,65 @@ static int sde_plane_rot_atomic_check(struct drm_plane *plane,
pstate = to_sde_plane_state(state);
old_pstate = to_sde_plane_state(plane->state);
- /* cstate will be null if crtc is disconnected from plane */
- cstate = _sde_plane_get_crtc_state(state);
- if (IS_ERR(cstate)) {
- ret = PTR_ERR(cstate);
- SDE_ERROR("invalid crtc state %d\n", ret);
- return ret;
- }
-
+ /* check inline rotation and simplify the transform */
pstate->rotation = drm_rotation_simplify(
state->rotation,
DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y);
+ if ((pstate->rotation & DRM_MODE_ROTATE_180) ||
+ (pstate->rotation & DRM_MODE_ROTATE_270)) {
+ SDE_ERROR_PLANE(psde,
+ "invalid rotation transform must be simplified 0x%x\n",
+ pstate->rotation);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ msm_fmt = msm_framebuffer_format(state->fb);
+ fmt = to_sde_format(msm_fmt);
+ height = state->fb ? state->fb->height : 0x0;
+
+ if ((pstate->rotation & DRM_MODE_ROTATE_90)) {
+ struct msm_drm_private *priv = plane->dev->dev_private;
+ struct sde_kms *sde_kms;
+
+ if (!psde->pipe_sblk->in_rot_maxdwnscale_rt ||
+ !psde->pipe_sblk->in_rot_maxdwnscale_nrt ||
+ !psde->pipe_sblk->in_rot_maxheight ||
+ !psde->pipe_sblk->in_rot_format_list ||
+ !(psde->features & BIT(SDE_SSPP_TRUE_INLINE_ROT_V1))) {
+ SDE_ERROR_PLANE(psde,
+ "wrong config rt:%d nrt:%d fmt:%d h:%d 0x%x\n",
+ !psde->pipe_sblk->in_rot_maxdwnscale_rt,
+ !psde->pipe_sblk->in_rot_maxdwnscale_nrt,
+ !psde->pipe_sblk->in_rot_format_list,
+ !psde->pipe_sblk->in_rot_maxheight,
+ psde->features);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* check for valid height */
+ if (height > psde->pipe_sblk->in_rot_maxheight) {
+ SDE_ERROR_PLANE(psde,
+ "invalid height for inline rot:%d max:%d\n",
+ height, psde->pipe_sblk->in_rot_maxheight);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (!sde_plane_enabled(state))
+ goto exit;
+
+ /* check for valid formats supported by inline rot */
+ sde_kms = to_sde_kms(priv->kms);
+ ret = sde_format_validate_fmt(&sde_kms->base, msm_fmt,
+ psde->pipe_sblk->in_rot_format_list);
+
+ }
+
+exit:
return ret;
}
@@ -1716,6 +1761,10 @@ static void sde_plane_rot_install_properties(struct drm_plane *plane,
return;
}
+ if (psde->features & BIT(SDE_SSPP_TRUE_INLINE_ROT_V1))
+ supported_rotations |= DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270;
+
ret = drm_plane_create_rotation_property(plane,
DRM_MODE_ROTATE_0, supported_rotations);
if (ret) {
@@ -2140,6 +2189,10 @@ static void _sde_plane_sspp_atomic_check_mode_changed(struct sde_plane *psde,
pstate->excl_rect.y != old_pstate->excl_rect.y) {
SDE_DEBUG_PLANE(psde, "excl_rect updated\n");
pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
+ } else if (pstate->rotation != old_pstate->rotation) {
+ SDE_DEBUG_PLANE(psde, "rotation updated 0x%x->0x%x\n",
+ pstate->rotation, old_pstate->rotation);
+ pstate->dirty |= SDE_PLANE_DIRTY_FORMAT;
}
fb = state->fb;
@@ -2333,14 +2386,18 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane,
int ret = 0;
struct sde_plane *psde;
struct sde_plane_state *pstate;
+ const struct msm_format *msm_fmt;
const struct sde_format *fmt;
struct sde_rect src, dst;
uint32_t deci_w, deci_h, src_deci_w, src_deci_h;
+ uint32_t scaler_src_w, scaler_src_h;
uint32_t max_upscale, max_downscale, min_src_size, max_linewidth;
bool q16_data = true;
struct drm_framebuffer *fb;
u32 width;
u32 height;
+ bool inline_rotation, rt_client;
+ struct drm_crtc *crtc;
if (!plane || !state) {
SDE_ERROR("invalid arg(s), plane %d state %d\n",
@@ -2370,10 +2427,35 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane,
src_deci_w = DECIMATED_DIMENSION(src.w, deci_w);
src_deci_h = DECIMATED_DIMENSION(src.h, deci_h);
+ /* with inline rotator, the source of the scaler is post-rotated */
+ inline_rotation = pstate->rotation & DRM_MODE_ROTATE_90 ? true : false;
+ if (inline_rotation) {
+ scaler_src_w = src_deci_h;
+ scaler_src_h = src_deci_w;
+ } else {
+ scaler_src_w = src_deci_w;
+ scaler_src_h = src_deci_h;
+ }
+
max_upscale = psde->pipe_sblk->maxupscale;
- max_downscale = psde->pipe_sblk->maxdwnscale;
max_linewidth = psde->pipe_sblk->maxlinewidth;
+ crtc = state->crtc;
+ if (crtc)
+ rt_client = (sde_crtc_get_client_type(crtc) != NRT_CLIENT);
+ else
+ rt_client = true;
+
+ /* inline rotation RT clients have a different max downscaling limit */
+ if (inline_rotation) {
+ if (rt_client)
+ max_downscale = psde->pipe_sblk->in_rot_maxdwnscale_rt;
+ else
+ max_downscale = psde->pipe_sblk->in_rot_maxdwnscale_nrt;
+ } else {
+ max_downscale = psde->pipe_sblk->maxdwnscale;
+ }
+
SDE_DEBUG_PLANE(psde, "check %d -> %d\n",
sde_plane_enabled(plane->state), sde_plane_enabled(state));
@@ -2390,13 +2472,14 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane,
width, height,
fb ? (char *) &state->fb->format->format : 0x0,
fb ? state->fb->modifier : 0x0);
- SDE_DEBUG("src:%dx%d %d,%d crtc:%dx%d+%d+%d\n"
+ SDE_DEBUG("src:%dx%d %d,%d crtc:%dx%d+%d+%d\n",
state->src_w >> 16, state->src_h >> 16,
state->src_x >> 16, state->src_y >> 16,
state->crtc_w, state->crtc_h,
state->crtc_x, state->crtc_y);
- fmt = to_sde_format(msm_framebuffer_format(state->fb));
+ msm_fmt = msm_framebuffer_format(fb);
+ fmt = to_sde_format(msm_fmt);
min_src_size = SDE_FORMAT_IS_YUV(fmt) ? 2 : 1;
@@ -2458,13 +2541,14 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane,
ret = -E2BIG;
/* check max scaler capability */
- } else if (((src_deci_w * max_upscale) < dst.w) ||
- ((src_deci_h * max_upscale) < dst.h) ||
- ((dst.w * max_downscale) < src_deci_w) ||
- ((dst.h * max_downscale) < src_deci_h)) {
+ } else if (((scaler_src_w * max_upscale) < dst.w) ||
+ ((scaler_src_h * max_upscale) < dst.h) ||
+ ((dst.w * max_downscale) < scaler_src_w) ||
+ ((dst.h * max_downscale) < scaler_src_h)) {
SDE_ERROR_PLANE(psde,
- "too much scaling requested %ux%u->%ux%u\n",
- src_deci_w, src_deci_h, dst.w, dst.h);
+ "too much scaling requested %ux%u->%ux%u rot:%d\n",
+ scaler_src_w, scaler_src_h, dst.w, dst.h,
+ inline_rotation);
ret = -E2BIG;
} else if (_sde_plane_validate_scaler_v2(psde, pstate, fmt,
width, height,
@@ -2585,6 +2669,34 @@ void sde_plane_set_error(struct drm_plane *plane, bool error)
psde->is_error = error;
}
+static void _sde_plane_sspp_setup_sys_cache(struct sde_plane *psde,
+ struct sde_plane_state *pstate, const struct sde_format *fmt)
+{
+ if (psde->pipe_hw->ops.setup_sys_cache &&
+ (psde->perf_features & BIT(SDE_PERF_SSPP_SYS_CACHE))) {
+
+ SDE_DEBUG("features:0x%x rotation:0x%x\n",
+ __func__, psde->features, pstate->rotation);
+
+ if ((pstate->rotation & DRM_MODE_ROTATE_90) &&
+ sde_format_is_tp10_ubwc(fmt)) {
+ pstate->sc_cfg.rd_en = true;
+ pstate->sc_cfg.rd_scid =
+ psde->pipe_sblk->llcc_scid;
+ pstate->sc_cfg.flags = SSPP_SYS_CACHE_EN_FLAG |
+ SSPP_SYS_CACHE_SCID;
+ } else {
+ pstate->sc_cfg.rd_en = false;
+ pstate->sc_cfg.rd_scid = 0x0;
+ pstate->sc_cfg.flags = SSPP_SYS_CACHE_EN_FLAG |
+ SSPP_SYS_CACHE_SCID;
+ }
+
+ psde->pipe_hw->ops.setup_sys_cache(
+ psde->pipe_hw, &pstate->sc_cfg);
+ }
+}
+
static int sde_plane_sspp_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
@@ -2835,12 +2947,17 @@ static int sde_plane_sspp_atomic_update(struct drm_plane *plane,
if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT ||
pstate->dirty & SDE_PLANE_DIRTY_RECTS) &&
psde->pipe_hw->ops.setup_format) {
+
src_flags = pstate->pipe_order_flags;
- SDE_DEBUG_PLANE(psde, "rotation 0x%X\n", pstate->rotation);
+
+ SDE_DEBUG_PLANE(psde, "rotation 0x%x features:0x%x\n",
+ pstate->rotation, psde->features);
if (pstate->rotation & DRM_MODE_REFLECT_X)
src_flags |= SDE_SSPP_FLIP_LR;
if (pstate->rotation & DRM_MODE_REFLECT_Y)
src_flags |= SDE_SSPP_FLIP_UD;
+ if (pstate->rotation & DRM_MODE_ROTATE_90)
+ src_flags |= SDE_SSPP_ROT_90;
/* update format */
psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt,
@@ -2865,6 +2982,8 @@ static int sde_plane_sspp_atomic_update(struct drm_plane *plane,
pstate->multirect_index);
}
+ _sde_plane_sspp_setup_sys_cache(psde, pstate, fmt);
+
/* update csc */
if (SDE_FORMAT_IS_YUV(fmt))
_sde_plane_setup_csc(psde);
@@ -3007,6 +3126,24 @@ void sde_plane_restore(struct drm_plane *plane)
sde_plane_atomic_update(plane, plane->state);
}
+bool sde_plane_is_cache_required(struct drm_plane *plane)
+{
+ struct sde_plane_state *pstate;
+
+ if (!plane || !plane->state) {
+ SDE_ERROR("invalid plane\n");
+ return false;
+ }
+
+ pstate = to_sde_plane_state(plane->state);
+
+ /* check if llcc is required for the plane */
+ if (pstate->sc_cfg.rd_en)
+ return true;
+ else
+ return false;
+}
+
/* helper to install properties which are common to planes and crtcs */
static void _sde_plane_install_properties(struct drm_plane *plane,
struct sde_mdss_cfg *catalog, u32 master_plane_id)
@@ -3241,6 +3378,32 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
if (psde->features & BIT(SDE_SSPP_BLOCK_SEC_UI))
sde_kms_info_add_keyint(info, "block_sec_ui", 1);
+ if (psde->features & BIT(SDE_SSPP_TRUE_INLINE_ROT_V1)) {
+ const struct sde_format_extended *inline_rot_fmt_list;
+
+ sde_kms_info_add_keyint(info, "true_inline_rot_rev", 1);
+ sde_kms_info_add_keyint(info, "true_inline_dwnscale_rt",
+ psde->pipe_sblk->in_rot_maxdwnscale_rt);
+ sde_kms_info_add_keyint(info, "true_inline_dwnscale_nrt",
+ psde->pipe_sblk->in_rot_maxdwnscale_nrt);
+ sde_kms_info_add_keyint(info, "true_inline_max_height",
+ psde->pipe_sblk->in_rot_maxheight);
+
+ inline_rot_fmt_list = psde->pipe_sblk->in_rot_format_list;
+
+ if (inline_rot_fmt_list) {
+ sde_kms_info_start(info, "inline_rot_pixel_formats");
+ while (inline_rot_fmt_list->fourcc_format) {
+ sde_kms_info_append_format(info,
+ inline_rot_fmt_list->fourcc_format,
+ inline_rot_fmt_list->modifier);
+ ++inline_rot_fmt_list;
+ }
+ sde_kms_info_stop(info);
+ }
+
+ }
+
msm_property_set_blob(&psde->property_info, &psde->blob_info,
info->data, SDE_KMS_INFO_DATALEN(info),
PLANE_PROP_INFO);
@@ -3943,6 +4106,21 @@ static int _sde_plane_init_debugfs(struct drm_plane *plane)
psde->debugfs_root,
&psde->debugfs_default_scale);
+ if (cfg->features & BIT(SDE_SSPP_TRUE_INLINE_ROT_V1)) {
+ debugfs_create_u32("in_rot_max_downscale_rt",
+ 0600,
+ psde->debugfs_root,
+ (u32 *) &psde->pipe_sblk->in_rot_maxdwnscale_rt);
+ debugfs_create_u32("in_rot_max_downscale_nrt",
+ 0600,
+ psde->debugfs_root,
+ (u32 *) &psde->pipe_sblk->in_rot_maxdwnscale_nrt);
+ debugfs_create_u32("in_rot_max_height",
+ 0600,
+ psde->debugfs_root,
+ (u32 *) &psde->pipe_sblk->in_rot_maxheight);
+ }
+
debugfs_create_u32("xin_id",
0400,
psde->debugfs_root,
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index ca5c4a6..2be78d6 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -285,4 +285,11 @@ u32 sde_plane_get_ubwc_error(struct drm_plane *plane);
*/
void sde_plane_clear_ubwc_error(struct drm_plane *plane);
+/* sde_plane_is_cache_required - indicates if the system cache is
+ * required for the plane.
+ * @plane: Pointer to DRM plane object
+ * Returns: true if sys cache is required, otherwise false.
+ */
+bool sde_plane_is_cache_required(struct drm_plane *plane);
+
#endif /* _SDE_PLANE_H_ */
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index cf82db7..8bf68b3 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (c) 2012-2014, 2016-2017 The Linux Foundation. All rights reserved.
*/
#include <linux/gpio.h>
@@ -137,6 +129,7 @@ enum pmic_gpio_func_index {
* struct pmic_gpio_pad - keep current GPIO settings
* @base: Address base in SPMI device.
* @irq: IRQ number which this GPIO generate.
+ * @gpio_idx: The index in GPIO's hardware number space (1-based)
* @is_enabled: Set to false when GPIO should be put in high Z state.
* @out_value: Cached pin output value
* @have_buffer: Set to true if GPIO output could be configured in push-pull,
@@ -157,6 +150,7 @@ enum pmic_gpio_func_index {
struct pmic_gpio_pad {
u16 base;
int irq;
+ int gpio_idx;
bool is_enabled;
bool out_value;
bool have_buffer;
@@ -179,6 +173,7 @@ struct pmic_gpio_state {
struct regmap *map;
struct pinctrl_dev *ctrl;
struct gpio_chip chip;
+ const char **gpio_groups;
};
static const struct pinconf_generic_params pmic_gpio_bindings[] = {
@@ -292,7 +287,9 @@ static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev,
const char *const **groups,
unsigned *const num_qgroups)
{
- *groups = pmic_gpio_groups;
+ struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = state->gpio_groups;
*num_qgroups = pctldev->desc->npins;
return 0;
}
@@ -432,6 +429,9 @@ static int pmic_gpio_config_get(struct pinctrl_dev *pctldev,
return -EINVAL;
arg = 1;
break;
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ arg = pad->output_enabled;
+ break;
case PIN_CONFIG_OUTPUT:
arg = pad->out_value;
break;
@@ -511,6 +511,9 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
case PIN_CONFIG_INPUT_ENABLE:
pad->input_enabled = arg ? true : false;
break;
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ pad->output_enabled = arg ? true : false;
+ break;
case PIN_CONFIG_OUTPUT:
pad->output_enabled = true;
pad->out_value = arg;
@@ -644,7 +647,7 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
pad = pctldev->desc->pins[pin].drv_data;
- seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET);
+ seq_printf(s, " gpio%-2d:", pad->gpio_idx);
val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL);
@@ -749,13 +752,29 @@ static int pmic_gpio_of_xlate(struct gpio_chip *chip,
const struct of_phandle_args *gpio_desc,
u32 *flags)
{
+ int i;
+ struct pmic_gpio_state *state = gpiochip_get_data(chip);
+ struct pinctrl_desc *desc = state->ctrl->desc;
+ struct pmic_gpio_pad *pad;
+
if (chip->of_gpio_n_cells < 2)
return -EINVAL;
if (flags)
*flags = gpio_desc->args[1];
- return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
+ for (i = 0; i < chip->ngpio; i++) {
+ pad = desc->pins[i].drv_data;
+ if (pad->gpio_idx == gpio_desc->args[0]) {
+ dev_dbg(state->dev, "gpio%-2d xlate to pin%-2d\n",
+ gpio_desc->args[0], i);
+ return i;
+ }
+ }
+
+ dev_err(state->dev, "Couldn't find pin for gpio %d\n",
+ gpio_desc->args[0]);
+ return -ENODEV;
}
static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
@@ -942,43 +961,124 @@ static int pmic_gpio_probe(struct platform_device *pdev)
struct pinctrl_desc *pctrldesc;
struct pmic_gpio_pad *pad, *pads;
struct pmic_gpio_state *state;
- int ret, npins, i;
- u32 reg;
+ int ret, npins, ngpios, i, j, pin_idx;
+ int disallowed_count = 0;
+ u32 reg[2], start, size;
+ u32 *disallowed = NULL;
- ret = of_property_read_u32(dev->of_node, "reg", ®);
+ ret = of_property_read_u32_array(dev->of_node, "reg", reg, 2);
if (ret < 0) {
- dev_err(dev, "missing base address");
+ dev_err(dev, "reg property reading failed\n");
return ret;
}
+ start = reg[0];
+ size = reg[1];
- npins = platform_irq_count(pdev);
- if (!npins)
+ ngpios = size / PMIC_GPIO_ADDRESS_RANGE;
+ if (ngpios == 0) {
+ dev_err(dev, "no gpios assigned\n");
+ return -ENODEV;
+ }
+
+ if (ngpios > ARRAY_SIZE(pmic_gpio_groups)) {
+ dev_err(dev, "reg property defines %d gpios, but only %d are allowed\n",
+ ngpios, (int)ARRAY_SIZE(pmic_gpio_groups));
return -EINVAL;
- if (npins < 0)
- return npins;
+ }
- BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups));
+ if (of_find_property(dev->of_node, "qcom,gpios-disallowed",
+ &disallowed_count)) {
+ disallowed_count /= sizeof(u32);
+ if (disallowed_count == 0) {
+ dev_err(dev, "No data in gpios-disallowed\n");
+ return -EINVAL;
+ }
+
+ disallowed = kcalloc(disallowed_count, sizeof(u32), GFP_KERNEL);
+ if (disallowed == NULL)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(dev->of_node,
+ "qcom,gpios-disallowed",
+ disallowed, disallowed_count);
+ if (ret < 0) {
+ dev_err(dev, "qcom,gpios-disallowed property reading failed, ret=%d\n",
+ ret);
+ goto err_free;
+ }
+
+ for (i = 0; i < disallowed_count; i++) {
+ if (disallowed[i] >= ngpios + PMIC_GPIO_PHYSICAL_OFFSET
+ || disallowed[i] < PMIC_GPIO_PHYSICAL_OFFSET) {
+ dev_err(dev, "invalid gpio = %d specified in qcom,gpios-disallowed, supported values: %d to %d\n",
+ disallowed[i],
+ PMIC_GPIO_PHYSICAL_OFFSET,
+ ngpios - 1 + PMIC_GPIO_PHYSICAL_OFFSET);
+ ret = -EINVAL;
+ goto err_free;
+ }
+ for (j = 0; j < i; j++) {
+ if (disallowed[i] == disallowed[j]) {
+ dev_err(dev, "duplicate gpio = %d listed in qcom,gpios-disallowed\n",
+ disallowed[i]);
+ ret = -EINVAL;
+ goto err_free;
+ }
+ }
+ dev_dbg(dev, "gpio %d NOT supported\n", disallowed[i]);
+ }
+ } else {
+ disallowed_count = 0;
+ }
+
+ npins = ngpios - disallowed_count;
+ if (npins <= 0) {
+ dev_err(dev, "No pins assigned\n");
+ ret = -ENODEV;
+ goto err_free;
+ }
+ if (platform_irq_count(pdev) != npins) {
+ dev_err(dev, "%d IRQs defined but %d expected\n",
+ platform_irq_count(pdev), npins);
+ ret = -EINVAL;
+ goto err_free;
+ }
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
+ if (!state) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
platform_set_drvdata(pdev, state);
state->dev = &pdev->dev;
state->map = dev_get_regmap(dev->parent, NULL);
+ state->gpio_groups = devm_kcalloc(dev, sizeof(*state->gpio_groups),
+ npins, GFP_KERNEL);
+ if (!state->gpio_groups) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
- if (!pindesc)
- return -ENOMEM;
+ if (!pindesc) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
- if (!pads)
- return -ENOMEM;
+ if (!pads) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
- if (!pctrldesc)
- return -ENOMEM;
+ if (!pctrldesc) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
pctrldesc->pctlops = &pmic_gpio_pinctrl_ops;
pctrldesc->pmxops = &pmic_gpio_pinmux_ops;
@@ -992,22 +1092,42 @@ static int pmic_gpio_probe(struct platform_device *pdev)
#ifdef CONFIG_DEBUG_FS
pctrldesc->custom_conf_items = pmic_conf_items;
#endif
+ for (pin_idx = 0, i = 0; i < ngpios; i++) {
+ for (j = 0; j < disallowed_count; j++) {
+ if (i + PMIC_GPIO_PHYSICAL_OFFSET == disallowed[j])
+ break;
+ }
+ if (j != disallowed_count)
+ continue;
- for (i = 0; i < npins; i++, pindesc++) {
- pad = &pads[i];
+ pad = &pads[pin_idx];
pindesc->drv_data = pad;
- pindesc->number = i;
+ pindesc->number = pin_idx;
pindesc->name = pmic_gpio_groups[i];
- pad->irq = platform_get_irq(pdev, i);
- if (pad->irq < 0)
- return pad->irq;
+ pad->gpio_idx = i + PMIC_GPIO_PHYSICAL_OFFSET;
+ pad->irq = platform_get_irq(pdev, pin_idx);
+ if (pad->irq < 0) {
+ dev_err(state->dev,
+ "failed to get irq for gpio %d (pin %d), ret=%d\n",
+ pad->gpio_idx, pin_idx, pad->irq);
+ ret = pad->irq;
+ goto err_free;
+ }
+ /* Every pin is a group */
+ state->gpio_groups[pin_idx] = pmic_gpio_groups[i];
- pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
+ pad->base = start + i * PMIC_GPIO_ADDRESS_RANGE;
ret = pmic_gpio_populate(state, pad);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ dev_err(state->dev,
+ "failed to populate gpio %d, ret=%d\n",
+ i, ret);
+ goto err_free;
+ }
+ pindesc++;
+ pin_idx++;
}
state->chip = pmic_gpio_gpio_template;
@@ -1019,25 +1139,29 @@ static int pmic_gpio_probe(struct platform_device *pdev)
state->chip.can_sleep = false;
state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
- if (IS_ERR(state->ctrl))
- return PTR_ERR(state->ctrl);
+ if (IS_ERR(state->ctrl)) {
+ ret = PTR_ERR(state->ctrl);
+ dev_err(state->dev, "failed to register pinctrl device, ret=%d\n",
+ ret);
+ goto err_free;
+ }
ret = gpiochip_add_data(&state->chip, state);
if (ret) {
- dev_err(state->dev, "can't add gpio chip\n");
- return ret;
+ dev_err(state->dev, "can't add gpio chip, ret=%d\n", ret);
+ goto err_free;
}
ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
if (ret) {
- dev_err(dev, "failed to add pin range\n");
- goto err_range;
+ dev_err(dev, "failed to add pin range\n, ret=%d\n", ret);
+ gpiochip_remove(&state->chip);
+ goto err_free;
}
- return 0;
+err_free:
+ kfree(disallowed);
-err_range:
- gpiochip_remove(&state->chip);
return ret;
}
diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c
index ad4f3a8..bda5af5 100644
--- a/drivers/thermal/qcom-spmi-temp-alarm.c
+++ b/drivers/thermal/qcom-spmi-temp-alarm.c
@@ -23,6 +23,7 @@
#include <linux/regmap.h>
#include <linux/thermal.h>
+#define QPNP_TM_REG_DIG_MAJOR 0x01
#define QPNP_TM_REG_TYPE 0x04
#define QPNP_TM_REG_SUBTYPE 0x05
#define QPNP_TM_REG_STATUS 0x08
@@ -42,20 +43,28 @@
#define ALARM_CTRL_FORCE_ENABLE BIT(7)
-/*
- * Trip point values based on threshold control
- * 0 = {105 C, 125 C, 145 C}
- * 1 = {110 C, 130 C, 150 C}
- * 2 = {115 C, 135 C, 155 C}
- * 3 = {120 C, 140 C, 160 C}
-*/
-#define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
+#define THRESH_COUNT 4
+#define STAGE_COUNT 3
+
+/* Over-temperature trip point values in mC */
+static const long temp_map_gen1[THRESH_COUNT][STAGE_COUNT] = {
+ {105000, 125000, 145000},
+ {110000, 130000, 150000},
+ {115000, 135000, 155000},
+ {120000, 140000, 160000},
+};
+
+static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = {
+ { 90000, 110000, 140000},
+ { 95000, 115000, 145000},
+ {100000, 120000, 150000},
+ {105000, 125000, 155000},
+};
+
#define TEMP_STAGE_HYSTERESIS 2000
-#define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
-#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
-
#define THRESH_MIN 0
+#define THRESH_MAX 3
/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
#define DEFAULT_TEMP 37000
@@ -69,7 +78,9 @@ struct qpnp_tm_chip {
unsigned int stage;
unsigned int prev_stage;
unsigned int base;
+ u32 init_thresh;
struct iio_channel *adc;
+ const long (*temp_map)[THRESH_COUNT][STAGE_COUNT];
};
/* This array maps from GEN2 alarm state to GEN1 alarm stage */
@@ -94,6 +105,23 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
}
/**
+ * qpnp_tm_decode_temp() - return temperature in mC corresponding to the
+ * specified over-temperature stage
+ * @chip: Pointer to the qpnp_tm chip
+ * @stage: Over-temperature stage
+ *
+ * Return: temperature in mC
+ */
+static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
+{
+ if (!chip->temp_map || chip->thresh >= THRESH_COUNT || stage == 0
+ || stage > STAGE_COUNT)
+ return 0;
+
+ return (*chip->temp_map)[chip->thresh][stage - 1];
+}
+
+/**
* qpnp_tm_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip
*
@@ -140,14 +168,12 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
if (stage_new > stage_old) {
/* increasing stage, use lower bound */
- chip->temp = (stage_new - 1) * TEMP_STAGE_STEP +
- chip->thresh * TEMP_THRESH_STEP +
- TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ chip->temp = qpnp_tm_decode_temp(chip, stage_new)
+ + TEMP_STAGE_HYSTERESIS;
} else if (stage_new < stage_old) {
/* decreasing stage, use upper bound */
- chip->temp = stage_new * TEMP_STAGE_STEP +
- chip->thresh * TEMP_THRESH_STEP -
- TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ chip->temp = qpnp_tm_decode_temp(chip, stage_new + 1)
+ - TEMP_STAGE_HYSTERESIS;
}
chip->stage = stage;
@@ -220,15 +246,13 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
? chip->stage : alarm_state_map[chip->stage];
if (stage)
- chip->temp = chip->thresh * TEMP_THRESH_STEP +
- (stage - 1) * TEMP_STAGE_STEP +
- TEMP_THRESH_MIN;
+ chip->temp = qpnp_tm_decode_temp(chip, stage);
/*
* Set threshold and disable software override of stage 2 and 3
* shutdowns.
*/
- chip->thresh = THRESH_MIN;
+ chip->thresh = chip->init_thresh;
reg &= ~(SHUTDOWN_CTRL1_OVERRIDE_MASK | SHUTDOWN_CTRL1_THRESHOLD_MASK);
reg |= chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
@@ -246,7 +270,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
{
struct qpnp_tm_chip *chip;
struct device_node *node;
- u8 type, subtype;
+ u8 type, subtype, dig_major;
u32 res;
int ret, irq;
@@ -266,6 +290,16 @@ static int qpnp_tm_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
+ chip->init_thresh = THRESH_MIN;
+ if (of_property_read_u32(node, "qcom,temperature-threshold-set",
+ &chip->init_thresh) == 0) {
+ if (chip->init_thresh > THRESH_MAX) {
+ dev_err(&pdev->dev, "Invalid qcom,temperature-threshold-set=%u\n",
+ chip->init_thresh);
+ return -EINVAL;
+ }
+ }
+
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
@@ -293,6 +327,12 @@ static int qpnp_tm_probe(struct platform_device *pdev)
return ret;
}
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MAJOR, &dig_major);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not read dig_major\n");
+ return ret;
+ }
+
if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
&& subtype != QPNP_TM_SUBTYPE_GEN2)) {
dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
@@ -302,6 +342,11 @@ static int qpnp_tm_probe(struct platform_device *pdev)
chip->subtype = subtype;
+ if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1)
+ chip->temp_map = &temp_map_gen2_v1;
+ else
+ chip->temp_map = &temp_map_gen1;
+
ret = qpnp_tm_init(chip);
if (ret < 0) {
dev_err(&pdev->dev, "init failed\n");