Staging: udlfb: Add functions to expose sysfs metrics and controls
Add sysfs controls for edid and performance metrics There are 8 new files exposed in /sys/class/graphics/fb* edid - returns 128 byte edid blog, suitable for parsing with parse-edid metrics_bytes_identical metrics_bytes_rendered metrics_bytes_sent metrics_cpu_kcycles_used metrics_misc and metrics_reset, which resets all perf metrics to zero The 6 perf metrics are of type atomic_t. So these metrics return precise results for short benchmarks, but any test approx a minute or longer runtime may roll over. Signed-off-by: Bernie Thompson <bernie@plugable.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
4574203f45
commit
7d9485e2c5
|
@ -818,6 +818,21 @@ static void dlfb_ops_fillrect(struct fb_info *info,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dlfb_get_edid(struct dlfb_data *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
char rbuf[2];
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(dev->edid); i++) {
|
||||||
|
ret = usb_control_msg(dev->udev,
|
||||||
|
usb_rcvctrlpipe(dev->udev, 0), (0x02),
|
||||||
|
(0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2,
|
||||||
|
0);
|
||||||
|
dev->edid[i] = rbuf[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd,
|
static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd,
|
||||||
unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
|
@ -924,6 +939,33 @@ static void dlfb_delete(struct kref *kref)
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check whether a video mode is supported by the DisplayLink chip
|
||||||
|
* We start from monitor's modes, so don't need to filter that here
|
||||||
|
*/
|
||||||
|
static int dlfb_is_valid_mode(struct fb_videomode *mode,
|
||||||
|
struct fb_info *info)
|
||||||
|
{
|
||||||
|
struct dlfb_data *dev = info->par;
|
||||||
|
|
||||||
|
if (mode->xres * mode->yres > dev->sku_pixel_limit)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dlfb_var_color_format(struct fb_var_screeninfo *var)
|
||||||
|
{
|
||||||
|
const struct fb_bitfield red = { 11, 5, 0 };
|
||||||
|
const struct fb_bitfield green = { 5, 6, 0 };
|
||||||
|
const struct fb_bitfield blue = { 0, 5, 0 };
|
||||||
|
|
||||||
|
var->bits_per_pixel = 16;
|
||||||
|
var->red = red;
|
||||||
|
var->green = green;
|
||||||
|
var->blue = blue;
|
||||||
|
}
|
||||||
|
|
||||||
static int dlfb_ops_blank(int blank_mode, struct fb_info *info)
|
static int dlfb_ops_blank(int blank_mode, struct fb_info *info)
|
||||||
{
|
{
|
||||||
struct dlfb_data *dev_info = info->par;
|
struct dlfb_data *dev_info = info->par;
|
||||||
|
@ -953,6 +995,215 @@ static struct fb_ops dlfb_ops = {
|
||||||
.fb_blank = dlfb_ops_blank,
|
.fb_blank = dlfb_ops_blank,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calls dlfb_get_edid() to query the EDID of attached monitor via usb cmds
|
||||||
|
* Then parses EDID into three places used by various parts of fbdev:
|
||||||
|
* fb_var_screeninfo contains the timing of the monitor's preferred mode
|
||||||
|
* fb_info.monspecs is full parsed EDID info, including monspecs.modedb
|
||||||
|
* fb_info.modelist is a linked list of all monitor & VESA modes which work
|
||||||
|
*
|
||||||
|
* If EDID is not readable/valid, then modelist is all VESA modes,
|
||||||
|
* monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode
|
||||||
|
* Returns 0 if EDID parses successfully
|
||||||
|
*/
|
||||||
|
static int dlfb_parse_edid(struct dlfb_data *dev,
|
||||||
|
struct fb_var_screeninfo *var,
|
||||||
|
struct fb_info *info)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const struct fb_videomode *default_vmode = NULL;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
fb_destroy_modelist(&info->modelist);
|
||||||
|
memset(&info->monspecs, 0, sizeof(info->monspecs));
|
||||||
|
|
||||||
|
dlfb_get_edid(dev);
|
||||||
|
fb_edid_to_monspecs(dev->edid, &info->monspecs);
|
||||||
|
|
||||||
|
if (info->monspecs.modedb_len > 0) {
|
||||||
|
|
||||||
|
for (i = 0; i < info->monspecs.modedb_len; i++) {
|
||||||
|
if (dlfb_is_valid_mode(&info->monspecs.modedb[i], info))
|
||||||
|
fb_add_videomode(&info->monspecs.modedb[i],
|
||||||
|
&info->modelist);
|
||||||
|
}
|
||||||
|
|
||||||
|
default_vmode = fb_find_best_display(&info->monspecs,
|
||||||
|
&info->modelist);
|
||||||
|
} else {
|
||||||
|
struct fb_videomode fb_vmode = {0};
|
||||||
|
|
||||||
|
dl_err("Unable to get valid EDID from device/display\n");
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the standard VESA modes to our modelist
|
||||||
|
* Since we don't have EDID, there may be modes that
|
||||||
|
* overspec monitor and/or are incorrect aspect ratio, etc.
|
||||||
|
* But at least the user has a chance to choose
|
||||||
|
*/
|
||||||
|
for (i = 0; i < VESA_MODEDB_SIZE; i++) {
|
||||||
|
if (dlfb_is_valid_mode((struct fb_videomode *)
|
||||||
|
&vesa_modes[i], info))
|
||||||
|
fb_add_videomode(&vesa_modes[i],
|
||||||
|
&info->modelist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* default to resolution safe for projectors
|
||||||
|
* (since they are most common case without EDID)
|
||||||
|
*/
|
||||||
|
fb_vmode.xres = 800;
|
||||||
|
fb_vmode.yres = 600;
|
||||||
|
fb_vmode.refresh = 60;
|
||||||
|
default_vmode = fb_find_nearest_mode(&fb_vmode,
|
||||||
|
&info->modelist);
|
||||||
|
}
|
||||||
|
|
||||||
|
fb_videomode_to_var(var, default_vmode);
|
||||||
|
dlfb_var_color_format(var);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t metrics_bytes_rendered_show(struct device *fbdev,
|
||||||
|
struct device_attribute *a, char *buf) {
|
||||||
|
struct fb_info *fb_info = dev_get_drvdata(fbdev);
|
||||||
|
struct dlfb_data *dev = fb_info->par;
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||||
|
atomic_read(&dev->bytes_rendered));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t metrics_bytes_identical_show(struct device *fbdev,
|
||||||
|
struct device_attribute *a, char *buf) {
|
||||||
|
struct fb_info *fb_info = dev_get_drvdata(fbdev);
|
||||||
|
struct dlfb_data *dev = fb_info->par;
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||||
|
atomic_read(&dev->bytes_identical));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t metrics_bytes_sent_show(struct device *fbdev,
|
||||||
|
struct device_attribute *a, char *buf) {
|
||||||
|
struct fb_info *fb_info = dev_get_drvdata(fbdev);
|
||||||
|
struct dlfb_data *dev = fb_info->par;
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||||
|
atomic_read(&dev->bytes_sent));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev,
|
||||||
|
struct device_attribute *a, char *buf) {
|
||||||
|
struct fb_info *fb_info = dev_get_drvdata(fbdev);
|
||||||
|
struct dlfb_data *dev = fb_info->par;
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||||
|
atomic_read(&dev->cpu_kcycles_used));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t metrics_misc_show(struct device *fbdev,
|
||||||
|
struct device_attribute *a, char *buf) {
|
||||||
|
struct fb_info *fb_info = dev_get_drvdata(fbdev);
|
||||||
|
struct dlfb_data *dev = fb_info->par;
|
||||||
|
return snprintf(buf, PAGE_SIZE,
|
||||||
|
"Calls to\ndamage: %u\nblit: %u\n"
|
||||||
|
"defio faults: %u\ncopy: %u\n"
|
||||||
|
"fill: %u\n\n"
|
||||||
|
"active framebuffer clients: %d\n"
|
||||||
|
"urbs available %d(%d)\n"
|
||||||
|
"Shadow framebuffer in use? %s\n"
|
||||||
|
"Any lost pixels? %s\n",
|
||||||
|
atomic_read(&dev->damage_count),
|
||||||
|
atomic_read(&dev->blit_count),
|
||||||
|
atomic_read(&dev->defio_fault_count),
|
||||||
|
atomic_read(&dev->copy_count),
|
||||||
|
atomic_read(&dev->fill_count),
|
||||||
|
dev->fb_count,
|
||||||
|
dev->urbs.available, dev->urbs.limit_sem.count,
|
||||||
|
(dev->backing_buffer) ? "yes" : "no",
|
||||||
|
atomic_read(&dev->lost_pixels) ? "yes" : "no");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *a,
|
||||||
|
char *buf, loff_t off, size_t count) {
|
||||||
|
struct device *fbdev = container_of(kobj, struct device, kobj);
|
||||||
|
struct fb_info *fb_info = dev_get_drvdata(fbdev);
|
||||||
|
struct dlfb_data *dev = fb_info->par;
|
||||||
|
char *edid = &dev->edid[0];
|
||||||
|
const size_t size = sizeof(dev->edid);
|
||||||
|
|
||||||
|
if (dlfb_parse_edid(dev, &fb_info->var, fb_info))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (off >= size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (off + count > size)
|
||||||
|
count = size - off;
|
||||||
|
memcpy(buf, edid + off, count);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ssize_t metrics_reset_store(struct device *fbdev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct fb_info *fb_info = dev_get_drvdata(fbdev);
|
||||||
|
struct dlfb_data *dev = fb_info->par;
|
||||||
|
|
||||||
|
atomic_set(&dev->bytes_rendered, 0);
|
||||||
|
atomic_set(&dev->bytes_identical, 0);
|
||||||
|
atomic_set(&dev->bytes_sent, 0);
|
||||||
|
atomic_set(&dev->cpu_kcycles_used, 0);
|
||||||
|
atomic_set(&dev->blit_count, 0);
|
||||||
|
atomic_set(&dev->copy_count, 0);
|
||||||
|
atomic_set(&dev->fill_count, 0);
|
||||||
|
atomic_set(&dev->defio_fault_count, 0);
|
||||||
|
atomic_set(&dev->damage_count, 0);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t use_defio_show(struct device *fbdev,
|
||||||
|
struct device_attribute *a, char *buf) {
|
||||||
|
struct fb_info *fb_info = dev_get_drvdata(fbdev);
|
||||||
|
struct dlfb_data *dev = fb_info->par;
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||||
|
atomic_read(&dev->use_defio));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t use_defio_store(struct device *fbdev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct fb_info *fb_info = dev_get_drvdata(fbdev);
|
||||||
|
struct dlfb_data *dev = fb_info->par;
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
if (buf[0] == '0')
|
||||||
|
atomic_set(&dev->use_defio, 0);
|
||||||
|
if (buf[0] == '1')
|
||||||
|
atomic_set(&dev->use_defio, 1);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bin_attribute edid_attr = {
|
||||||
|
.attr.name = "edid",
|
||||||
|
.attr.mode = 0444,
|
||||||
|
.size = 128,
|
||||||
|
.read = edid_show,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_attribute fb_device_attrs[] = {
|
||||||
|
__ATTR_RO(metrics_bytes_rendered),
|
||||||
|
__ATTR_RO(metrics_bytes_identical),
|
||||||
|
__ATTR_RO(metrics_bytes_sent),
|
||||||
|
__ATTR_RO(metrics_cpu_kcycles_used),
|
||||||
|
__ATTR_RO(metrics_misc),
|
||||||
|
__ATTR(metrics_reset, S_IWUGO, NULL, metrics_reset_store),
|
||||||
|
__ATTR_RW(use_defio),
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is necessary before we can communicate with the display controller.
|
* This is necessary before we can communicate with the display controller.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -33,8 +33,12 @@ struct dlfb_data {
|
||||||
char *bufend;
|
char *bufend;
|
||||||
char *backing_buffer;
|
char *backing_buffer;
|
||||||
struct mutex bulk_mutex;
|
struct mutex bulk_mutex;
|
||||||
|
int fb_count;
|
||||||
|
atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */
|
||||||
atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */
|
atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */
|
||||||
|
atomic_t use_defio; /* 0 = rely on ioctls and blit/copy/fill rects */
|
||||||
char edid[128];
|
char edid[128];
|
||||||
|
int sku_pixel_limit;
|
||||||
int screen_size;
|
int screen_size;
|
||||||
int line_length;
|
int line_length;
|
||||||
struct completion done;
|
struct completion done;
|
||||||
|
@ -43,6 +47,17 @@ struct dlfb_data {
|
||||||
int base8;
|
int base8;
|
||||||
int base8d;
|
int base8d;
|
||||||
u32 pseudo_palette[256];
|
u32 pseudo_palette[256];
|
||||||
|
/* blit-only rendering path metrics, exposed through sysfs */
|
||||||
|
atomic_t bytes_rendered; /* raw pixel-bytes driver asked to render */
|
||||||
|
atomic_t bytes_identical; /* saved effort with backbuffer comparison */
|
||||||
|
atomic_t bytes_sent; /* to usb, after compression including overhead */
|
||||||
|
atomic_t cpu_kcycles_used; /* transpired during pixel processing */
|
||||||
|
/* interface usage metrics. Clients can call driver via several */
|
||||||
|
atomic_t blit_count;
|
||||||
|
atomic_t copy_count;
|
||||||
|
atomic_t fill_count;
|
||||||
|
atomic_t damage_count;
|
||||||
|
atomic_t defio_fault_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NR_USB_REQUEST_I2C_SUB_IO 0x02
|
#define NR_USB_REQUEST_I2C_SUB_IO 0x02
|
||||||
|
@ -99,6 +114,9 @@ static int dlfb_bulk_msg(struct dlfb_data *dev_info, int len)
|
||||||
|
|
||||||
#define dlfb_set_register insert_command
|
#define dlfb_set_register insert_command
|
||||||
|
|
||||||
|
/* remove once this gets added to sysfs.h */
|
||||||
|
#define __ATTR_RW(attr) __ATTR(attr, 0644, attr##_show, attr##_store)
|
||||||
|
|
||||||
#define dl_err(format, arg...) \
|
#define dl_err(format, arg...) \
|
||||||
dev_err(dev->gdev, "dlfb: " format, ## arg)
|
dev_err(dev->gdev, "dlfb: " format, ## arg)
|
||||||
#define dl_warn(format, arg...) \
|
#define dl_warn(format, arg...) \
|
||||||
|
|
Loading…
Reference in a new issue