blob: 576f54f198689c858022f8fb9eda542da6d9af4b [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
#include <linux/backlight.h>
#include <linux/err.h>
#include <drm/drm_dp_aux_backlight.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_print.h>
/**
* drm_dp_aux_backlight_enable() - Enable DP aux backlight
* @aux_bl: the DP aux backlight to enable
*
* Returns 0 on success or a negative error code on failure.
*/
static int drm_dp_aux_backlight_enable(struct drm_dp_aux_backlight *aux_bl)
{
u8 val = 0;
int ret;
/* Set backlight control mode */
ret = drm_dp_dpcd_readb(aux_bl->aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER,
&val);
if (ret < 0)
return ret;
val &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
val |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
ret = drm_dp_dpcd_writeb(aux_bl->aux,
DP_EDP_BACKLIGHT_MODE_SET_REGISTER, val);
if (ret < 0)
return ret;
/* Enable backlight */
ret = drm_dp_dpcd_readb(aux_bl->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
&val);
if (ret < 0)
return ret;
val |= DP_EDP_BACKLIGHT_ENABLE;
ret = drm_dp_dpcd_writeb(aux_bl->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
val);
if (ret < 0)
return ret;
return 0;
}
/**
* drm_dp_aux_backlight_disable() - Disable DP aux backlight
* @aux_bl: the DP aux backlight to disable
*
* Returns 0 on success or a negative error code on failure.
*/
static int drm_dp_aux_backlight_disable(struct drm_dp_aux_backlight *aux_bl)
{
u8 val = 0;
int ret;
ret = drm_dp_dpcd_readb(aux_bl->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
&val);
if (ret < 0)
return ret;
val &= ~DP_EDP_BACKLIGHT_ENABLE;
ret = drm_dp_dpcd_writeb(aux_bl->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
val);
if (ret < 0)
return ret;
return 0;
}
static int drm_dp_aux_brightness_set(struct backlight_device *bd)
{
struct drm_dp_aux_backlight *pdata = bl_get_data(bd);
u16 brightness = bd->props.brightness;
u8 val[2] = { 0x0 };
int ret = 0;
bool is_blank;
is_blank = bd->props.power != FB_BLANK_UNBLANK ||
bd->props.fb_blank != FB_BLANK_UNBLANK ||
bd->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK);
if (is_blank) {
if (!pdata->enabled)
return 0;
ret = drm_dp_aux_backlight_disable(pdata);
if (ret)
return ret;
pdata->enabled = false;
} else {
/* Set the brightness before turning on the backlight. */
val[0] = brightness >> 8;
val[1] = brightness & 0xff;
ret = drm_dp_dpcd_write(pdata->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
val, sizeof(val));
if (ret < 0)
return ret;
/* If we're already enabled then we're done */
if (pdata->enabled)
return 0;
ret = drm_dp_aux_backlight_enable(pdata);
if (ret)
return ret;
pdata->enabled = true;
}
return 0;
}
static const struct backlight_ops aux_bl_ops = {
.update_status = drm_dp_aux_brightness_set,
};
/**
* drm_dp_aux_backlight_register() - register a DP aux backlight device
* @name: the name of the backlight device
* @aux_bl: the DP aux backlight to register
*
* Creates and registers a new backlight device that uses DPCD registers
* on the DisplayPort aux channel to control the brightness of the panel.
*
* Returns 0 on success or a negative error code on failure.
*/
int drm_dp_aux_backlight_register(const char *name,
struct drm_dp_aux_backlight *aux_bl)
{
struct backlight_properties bl_props = { 0 };
int max_brightness;
if (!name || !aux_bl || !aux_bl->aux)
return -EINVAL;
max_brightness = 0xffff;
bl_props.type = BACKLIGHT_RAW;
bl_props.brightness = max_brightness;
bl_props.max_brightness = max_brightness;
aux_bl->bd = devm_backlight_device_register(aux_bl->dev, name,
aux_bl->dev, aux_bl,
&aux_bl_ops, &bl_props);
if (IS_ERR(aux_bl->bd)) {
int ret = PTR_ERR(aux_bl->bd);
aux_bl->bd = NULL;
return ret;
}
/* Should start out officially "disabled" */
backlight_disable(aux_bl->bd);
return 0;
}
EXPORT_SYMBOL(drm_dp_aux_backlight_register);