Commit bc70cc84 authored by Thomas Zimmermann's avatar Thomas Zimmermann Committed by Lee Jones
Browse files

backlight: lcd: Replace fb events with a dedicated function call



Remove support for fb events from the lcd subsystem. Provide the
helper lcd_notify_blank_all() instead. In fbdev, call
lcd_notify_blank_all() to inform the lcd subsystem of changes
to a display's blank state.

Fbdev maintains a list of all installed notifiers. Instead of fbdev
notifiers, maintain an internal list of lcd devices.

v3:
- export lcd_notify_mode_change_all() (kernel test robot)
v2:
- maintain global list of lcd devices
- avoid IS_REACHABLE() in source file
- use lock guards
- initialize lcd list and list mutex

Signed-off-by: default avatarThomas Zimmermann <tzimmermann@suse.de>
Acked-by: default avatarSimona Vetter <simona.vetter@ffwll.ch>
Reviewed-by: default avatar"Daniel Thompson (RISCstar)" <danielt@kernel.org>
Link: https://lore.kernel.org/r/20250321095517.313713-9-tzimmermann@suse.de


Signed-off-by: default avatarLee Jones <lee@kernel.org>
parent e98696ce
Loading
Loading
Loading
Loading
+26 −72
Original line number Diff line number Diff line
@@ -15,9 +15,11 @@
#include <linux/notifier.h>
#include <linux/ctype.h>
#include <linux/err.h>
#include <linux/fb.h>
#include <linux/slab.h>

static DEFINE_MUTEX(lcd_dev_list_mutex);
static LIST_HEAD(lcd_dev_list);

static void lcd_notify_blank(struct lcd_device *ld, struct device *display_dev,
			     int power)
{
@@ -31,6 +33,17 @@ static void lcd_notify_blank(struct lcd_device *ld, struct device *display_dev,
	ld->ops->set_power(ld, power);
}

void lcd_notify_blank_all(struct device *display_dev, int power)
{
	struct lcd_device *ld;

	guard(mutex)(&lcd_dev_list_mutex);

	list_for_each_entry(ld, &lcd_dev_list, entry)
		lcd_notify_blank(ld, display_dev, power);
}
EXPORT_SYMBOL(lcd_notify_blank_all);

static void lcd_notify_mode_change(struct lcd_device *ld, struct device *display_dev,
				   unsigned int width, unsigned int height)
{
@@ -44,75 +57,17 @@ static void lcd_notify_mode_change(struct lcd_device *ld, struct device *display
	ld->ops->set_mode(ld, width, height);
}

#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
			   defined(CONFIG_LCD_CLASS_DEVICE_MODULE))
static int to_lcd_power(int fb_blank)
{
	switch (fb_blank) {
	case FB_BLANK_UNBLANK:
		return LCD_POWER_ON;
	/* deprecated; TODO: should become 'off' */
	case FB_BLANK_NORMAL:
		return LCD_POWER_REDUCED;
	case FB_BLANK_VSYNC_SUSPEND:
		return LCD_POWER_REDUCED_VSYNC_SUSPEND;
	/* 'off' */
	case FB_BLANK_HSYNC_SUSPEND:
	case FB_BLANK_POWERDOWN:
	default:
		return LCD_POWER_OFF;
	}
}

/* This callback gets called when something important happens inside a
 * framebuffer driver. We're looking if that important event is blanking,
 * and if it is, we're switching lcd power as well ...
 */
static int fb_notifier_callback(struct notifier_block *self,
				 unsigned long event, void *data)
{
	struct lcd_device *ld = container_of(self, struct lcd_device, fb_notif);
	struct fb_event *evdata = data;
	struct fb_info *info = evdata->info;
	struct lcd_device *fb_lcd = fb_lcd_device(info);

	if (fb_lcd && fb_lcd != ld)
		return 0;

	if (event == FB_EVENT_BLANK) {
		int power = to_lcd_power(*(int *)evdata->data);

		lcd_notify_blank(ld, info->device, power);
	} else {
		const struct fb_videomode *videomode = evdata->data;

		lcd_notify_mode_change(ld, info->device, videomode->xres, videomode->yres);
	}

	return 0;
}

static int lcd_register_fb(struct lcd_device *ld)
void lcd_notify_mode_change_all(struct device *display_dev,
				unsigned int width, unsigned int height)
{
	memset(&ld->fb_notif, 0, sizeof(ld->fb_notif));
	ld->fb_notif.notifier_call = fb_notifier_callback;
	return fb_register_client(&ld->fb_notif);
}
	struct lcd_device *ld;

static void lcd_unregister_fb(struct lcd_device *ld)
{
	fb_unregister_client(&ld->fb_notif);
}
#else
static int lcd_register_fb(struct lcd_device *ld)
{
	return 0;
}
	guard(mutex)(&lcd_dev_list_mutex);

static inline void lcd_unregister_fb(struct lcd_device *ld)
{
	list_for_each_entry(ld, &lcd_dev_list, entry)
		lcd_notify_mode_change(ld, display_dev, width, height);
}
#endif /* CONFIG_FB */
EXPORT_SYMBOL(lcd_notify_mode_change_all);

static ssize_t lcd_power_show(struct device *dev, struct device_attribute *attr,
		char *buf)
@@ -263,11 +218,8 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent,
		return ERR_PTR(rc);
	}

	rc = lcd_register_fb(new_ld);
	if (rc) {
		device_unregister(&new_ld->dev);
		return ERR_PTR(rc);
	}
	guard(mutex)(&lcd_dev_list_mutex);
	list_add(&new_ld->entry, &lcd_dev_list);

	return new_ld;
}
@@ -284,10 +236,12 @@ void lcd_device_unregister(struct lcd_device *ld)
	if (!ld)
		return;

	guard(mutex)(&lcd_dev_list_mutex);
	list_del(&ld->entry);

	mutex_lock(&ld->ops_lock);
	ld->ops = NULL;
	mutex_unlock(&ld->ops_lock);
	lcd_unregister_fb(ld);

	device_unregister(&ld->dev);
}
+35 −4
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/export.h>
#include <linux/fb.h>
#include <linux/fbcon.h>
#include <linux/lcd.h>

#include <video/nomodeset.h>

@@ -220,6 +221,12 @@ static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
	return err;
}

static void fb_lcd_notify_mode_change(struct fb_info *info,
				      struct fb_videomode *mode)
{
	lcd_notify_mode_change_all(info->device, mode->xres, mode->yres);
}

int
fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
{
@@ -227,7 +234,6 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
	u32 activate;
	struct fb_var_screeninfo old_var;
	struct fb_videomode mode;
	struct fb_event event;
	u32 unused;

	if (var->activate & FB_ACTIVATE_INV_MODE) {
@@ -331,14 +337,38 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
	if (ret)
		return ret;

	event.info = info;
	event.data = &mode;
	fb_notifier_call_chain(FB_EVENT_MODE_CHANGE, &event);
	fb_lcd_notify_mode_change(info, &mode);

	return 0;
}
EXPORT_SYMBOL(fb_set_var);

static void fb_lcd_notify_blank(struct fb_info *info)
{
	int power;

	switch (info->blank) {
	case FB_BLANK_UNBLANK:
		power = LCD_POWER_ON;
		break;
	/* deprecated; TODO: should become 'off' */
	case FB_BLANK_NORMAL:
		power = LCD_POWER_REDUCED;
		break;
	case FB_BLANK_VSYNC_SUSPEND:
		power = LCD_POWER_REDUCED_VSYNC_SUSPEND;
		break;
	/* 'off' */
	case FB_BLANK_HSYNC_SUSPEND:
	case FB_BLANK_POWERDOWN:
	default:
		power = LCD_POWER_OFF;
		break;
	}

	lcd_notify_blank_all(info->device, power);
}

int fb_blank(struct fb_info *info, int blank)
{
	int old_blank = info->blank;
@@ -364,6 +394,7 @@ int fb_blank(struct fb_info *info, int blank)
		goto err;

	fb_bl_notify_blank(info, old_blank);
	fb_lcd_notify_blank(info);

	fb_notifier_call_chain(FB_EVENT_BLANK, &event);

+18 −3
Original line number Diff line number Diff line
@@ -11,7 +11,6 @@

#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/notifier.h>

#define LCD_POWER_ON			(0)
#define LCD_POWER_REDUCED		(1) // deprecated; don't use in new code
@@ -79,8 +78,11 @@ struct lcd_device {
	const struct lcd_ops *ops;
	/* Serialise access to set_power method */
	struct mutex update_lock;
	/* The framebuffer notifier block */
	struct notifier_block fb_notif;

	/**
	 * @entry: List entry of all registered lcd devices
	 */
	struct list_head entry;

	struct device dev;
};
@@ -125,6 +127,19 @@ extern void lcd_device_unregister(struct lcd_device *ld);
extern void devm_lcd_device_unregister(struct device *dev,
	struct lcd_device *ld);

#if IS_REACHABLE(CONFIG_LCD_CLASS_DEVICE)
void lcd_notify_blank_all(struct device *display_dev, int power);
void lcd_notify_mode_change_all(struct device *display_dev,
				unsigned int width, unsigned int height);
#else
static inline void lcd_notify_blank_all(struct device *display_dev, int power)
{}

static inline void lcd_notify_mode_change_all(struct device *display_dev,
					      unsigned int width, unsigned int height)
{}
#endif

#define to_lcd_device(obj) container_of(obj, struct lcd_device, dev)

static inline void * lcd_get_data(struct lcd_device *ld_dev)