Expose Nouveau panel brightness through the connector LUMINANCE property
when DRM_LUMINANCE is enabled. Reuse the existing NV40, NV50, and DPCD
hardware paths while preserving the backlight class interface otherwise.

Signed-off-by: Mario Limonciello <[email protected]>
---
 drivers/gpu/drm/nouveau/Kconfig             |   4 +-
 drivers/gpu/drm/nouveau/dispnv50/disp.c     |   2 +-
 drivers/gpu/drm/nouveau/nouveau_backlight.c | 110 ++++++++++++++++----
 drivers/gpu/drm/nouveau/nouveau_connector.c |   2 +
 drivers/gpu/drm/nouveau/nouveau_connector.h |  14 +++
 5 files changed, 108 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
index 3b5757aed9c84..2dcb7c5344f48 100644
--- a/drivers/gpu/drm/nouveau/Kconfig
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -17,13 +17,13 @@ config DRM_NOUVEAU
        select DRM_SCHED
        select I2C
        select I2C_ALGOBIT
-       select BACKLIGHT_CLASS_DEVICE if DRM_NOUVEAU_BACKLIGHT
+       select BACKLIGHT_CLASS_DEVICE if DRM_NOUVEAU_BACKLIGHT && !DRM_LUMINANCE
        select X86_PLATFORM_DEVICES if ACPI && X86
        select ACPI_WMI if ACPI && X86
        select MXM_WMI if ACPI && X86
        select POWER_SUPPLY
        # Similar to i915, we need to select ACPI_VIDEO and it's dependencies
-       select BACKLIGHT_CLASS_DEVICE if ACPI && X86
+       select BACKLIGHT_CLASS_DEVICE if ACPI && X86 && !DRM_LUMINANCE
        select INPUT if ACPI && X86
        select THERMAL if ACPI && X86
        select ACPI_VIDEO if ACPI && X86
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c 
b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index add19d479ebe4..3810083daefdc 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -1839,7 +1839,7 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, 
struct drm_atomic_commit *st
                backlight = nv_connector->backlight;
                if (backlight && backlight->uses_dpcd)
                        drm_edp_backlight_enable(&nv_connector->aux, 
&backlight->edp_info,
-                                                
backlight->dev->props.brightness);
+                                                backlight->brightness);
 #endif
 
                break;
diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c 
b/drivers/gpu/drm/nouveau/nouveau_backlight.c
index da1ee24a2531d..fefcfa8c2a3a9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_backlight.c
+++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c
@@ -73,12 +73,10 @@ nv40_get_intensity(struct backlight_device *bd)
 }
 
 static int
-nv40_set_intensity(struct backlight_device *bd)
+nv40_set_intensity_value(struct nouveau_encoder *nv_encoder, int val)
 {
-       struct nouveau_encoder *nv_encoder = bl_get_data(bd);
        struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
        struct nvif_object *device = &drm->client.device.object;
-       int val = bd->props.brightness;
        int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
 
        nvif_wr32(device, NV40_PMC_BACKLIGHT,
@@ -87,6 +85,12 @@ nv40_set_intensity(struct backlight_device *bd)
        return 0;
 }
 
+static int
+nv40_set_intensity(struct backlight_device *bd)
+{
+       return nv40_set_intensity_value(bl_get_data(bd), bd->props.brightness);
+}
+
 static const struct backlight_ops nv40_bl_ops = {
        .options = BL_CORE_SUSPENDRESUME,
        .get_brightness = nv40_get_intensity,
@@ -105,6 +109,8 @@ nv40_backlight_init(struct nouveau_encoder *encoder,
                return -ENODEV;
 
        props->max_brightness = 31;
+       props->brightness = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
+                            NV40_PMC_BACKLIGHT_MASK) >> 16;
        *ops = &nv40_bl_ops;
        return 0;
 }
@@ -234,12 +240,15 @@ nv50_backlight_init(struct nouveau_backlight *bl,
                    const struct backlight_ops **ops)
 {
        struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
+       int brightness;
+
+       brightness = nvif_outp_bl_get(&nv_encoder->outp);
 
        /*
         * Note when this runs the connectors have not been probed yet,
         * so nv_conn->base.status is not set yet.
         */
-       if (nvif_outp_bl_get(&nv_encoder->outp) < 0 ||
+       if (brightness < 0 ||
            drm_helper_probe_detect(&nv_conn->base, NULL, false) != 
connector_status_connected)
                return -ENODEV;
 
@@ -284,6 +293,7 @@ nv50_backlight_init(struct nouveau_backlight *bl,
 
        *ops = &nv50_bl_ops;
        props->max_brightness = 100;
+       props->brightness = brightness;
        return 0;
 }
 
@@ -317,10 +327,13 @@ nouveau_backlight_init(struct drm_connector *connector)
        bl = kzalloc_obj(*bl);
        if (!bl)
                return -ENOMEM;
+       bl->id = -1;
+       bl->encoder = nv_encoder;
 
        switch (device->info.family) {
        case NV_DEVICE_INFO_V0_CURIE:
                ret = nv40_backlight_init(nv_encoder, &props, &ops);
+               bl->uses_nv40 = true;
                break;
        case NV_DEVICE_INFO_V0_TESLA:
        case NV_DEVICE_INFO_V0_FERMI:
@@ -349,26 +362,39 @@ nouveau_backlight_init(struct drm_connector *connector)
                goto fail_alloc;
        }
 
-       if (!nouveau_get_backlight_name(backlight_name, bl)) {
-               NV_ERROR(drm, "Failed to retrieve a unique name for the 
backlight interface\n");
-               goto fail_alloc;
-       }
-
        props.type = BACKLIGHT_RAW;
-       bl->dev = backlight_device_register(backlight_name, connector->kdev,
-                                           nv_encoder, ops, &props);
-       if (IS_ERR(bl->dev)) {
-               if (bl->id >= 0)
-                       ida_free(&bl_ida, bl->id);
-               ret = PTR_ERR(bl->dev);
-               goto fail_alloc;
+       bl->brightness = props.brightness;
+       bl->max_brightness = props.max_brightness;
+       if (IS_ENABLED(CONFIG_DRM_LUMINANCE)) {
+               ret = drm_connector_attach_luminance_property(connector, 0, 
props.max_brightness,
+                                                             props.brightness);
+               if (ret)
+                       goto fail_alloc;
+       } else {
+               if (!nouveau_get_backlight_name(backlight_name, bl)) {
+                       NV_ERROR(drm,
+                                "Failed to retrieve a unique name for the 
backlight interface\n");
+                       goto fail_alloc;
+               }
+
+               bl->dev = backlight_device_register(backlight_name, 
connector->kdev,
+                                                   nv_encoder, ops, &props);
+               if (IS_ERR(bl->dev)) {
+                       if (bl->id >= 0)
+                               ida_free(&bl_ida, bl->id);
+                       ret = PTR_ERR(bl->dev);
+                       goto fail_alloc;
+               }
        }
 
        nouveau_connector(connector)->backlight = bl;
-       if (!bl->dev->props.brightness)
-               bl->dev->props.brightness =
-                       bl->dev->ops->get_brightness(bl->dev);
-       backlight_update_status(bl->dev);
+       if (bl->dev) {
+               if (!bl->dev->props.brightness)
+                       bl->dev->props.brightness =
+                               bl->dev->ops->get_brightness(bl->dev);
+               bl->brightness = bl->dev->props.brightness;
+               backlight_update_status(bl->dev);
+       }
 
        return 0;
 
@@ -396,7 +422,9 @@ nouveau_backlight_fini(struct drm_connector *connector)
        if (bl->id >= 0)
                ida_free(&bl_ida, bl->id);
 
-       backlight_device_unregister(bl->dev);
+       if (!IS_ENABLED(CONFIG_DRM_LUMINANCE))
+               backlight_device_unregister(bl->dev);
+
        nv_conn->backlight = NULL;
        kfree(bl);
 }
@@ -412,3 +440,43 @@ nouveau_backlight_dtor(void)
 {
        ida_destroy(&bl_ida);
 }
+
+void
+nouveau_backlight_set_luminance(const struct drm_connector_state *conn_state)
+{
+       struct nouveau_connector *nv_connector = 
nouveau_connector(conn_state->connector);
+       struct nouveau_backlight *bl = nv_connector->backlight;
+       unsigned int luminance = conn_state->luminance;
+
+       if (!bl || !bl->max_brightness)
+               return;
+
+       bl->brightness = luminance;
+       if (bl->uses_dpcd) {
+               drm_edp_backlight_set_level(&nv_connector->aux, &bl->edp_info,
+                                           luminance);
+               return;
+       }
+
+       if (bl->uses_nv40)
+               nv40_set_intensity_value(bl->encoder, luminance);
+       else
+               nvif_outp_bl_set(&bl->encoder->outp, luminance);
+}
+
+void
+nouveau_backlight_disable_luminance(const struct drm_connector_state 
*conn_state)
+{
+       struct nouveau_connector *nv_connector = 
nouveau_connector(conn_state->connector);
+       struct nouveau_backlight *bl = nv_connector->backlight;
+
+       if (!bl)
+               return;
+
+       if (bl->uses_dpcd)
+               drm_edp_backlight_disable(&nv_connector->aux, &bl->edp_info);
+       else if (bl->uses_nv40)
+               nv40_set_intensity_value(bl->encoder, 0);
+       else
+               nvif_outp_bl_set(&bl->encoder->outp, 0);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c 
b/drivers/gpu/drm/nouveau/nouveau_connector.c
index cc3caf8de64b0..56ee579ea7909 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -1225,6 +1225,8 @@ nouveau_connector_helper_funcs = {
        .mode_valid = nouveau_connector_mode_valid,
        .best_encoder = nouveau_connector_best_encoder,
        .atomic_check = nouveau_connector_atomic_check,
+       .set_luminance = nouveau_backlight_set_luminance,
+       .disable_luminance = nouveau_backlight_disable_luminance,
 };
 
 static const struct drm_connector_funcs
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h 
b/drivers/gpu/drm/nouveau/nouveau_connector.h
index eb292d2ba4bcd..0cee3e4c1d11c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -50,9 +50,13 @@ struct nouveau_backlight {
        struct backlight_device *dev;
 
        struct drm_edp_backlight_info edp_info;
+       struct nouveau_encoder *encoder;
        bool uses_dpcd : 1;
+       bool uses_nv40 : 1;
 
        int id;
+       int brightness;
+       int max_brightness;
 };
 #endif
 
@@ -239,6 +243,8 @@ extern int nouveau_backlight_init(struct drm_connector *);
 extern void nouveau_backlight_fini(struct drm_connector *);
 extern void nouveau_backlight_ctor(void);
 extern void nouveau_backlight_dtor(void);
+void nouveau_backlight_set_luminance(const struct drm_connector_state 
*conn_state);
+void nouveau_backlight_disable_luminance(const struct drm_connector_state 
*conn_state);
 #else
 static inline int
 nouveau_backlight_init(struct drm_connector *connector)
@@ -257,6 +263,14 @@ nouveau_backlight_ctor(void) {
 static inline void
 nouveau_backlight_dtor(void) {
 }
+
+static inline void
+nouveau_backlight_set_luminance(const struct drm_connector_state *conn_state) {
+}
+
+static inline void
+nouveau_backlight_disable_luminance(const struct drm_connector_state 
*conn_state) {
+}
 #endif
 
 #endif /* __NOUVEAU_CONNECTOR_H__ */
-- 
2.43.0

Reply via email to