diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index ea8fa71c9e9c..4bea966e617b 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -413,6 +413,7 @@ config HID_VIVALDI_COMMON
 config HID_GOOGLE_HAMMER
 	tristate "Google Hammer Keyboard"
 	select HID_VIVALDI_COMMON
+	select INPUT_VIVALDIFMAP
 	depends on USB_HID && LEDS_CLASS && CROS_EC
 	help
 	Say Y here if you have a Google Hammer device.
diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c
index 0403beb3104b..f8e6dccf6ed8 100644
--- a/drivers/hid/hid-google-hammer.c
+++ b/drivers/hid/hid-google-hammer.c
@@ -15,6 +15,7 @@
 
 #include <linux/acpi.h>
 #include <linux/hid.h>
+#include <linux/input/vivaldi-fmap.h>
 #include <linux/leds.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -22,6 +23,7 @@
 #include <linux/platform_data/cros_ec_proto.h>
 #include <linux/platform_device.h>
 #include <linux/pm_wakeup.h>
+#include <linux/sysfs.h>
 #include <asm/unaligned.h>
 
 #include "hid-ids.h"
@@ -300,6 +302,11 @@ struct hammer_kbd_leds {
 	u8 buf[2] ____cacheline_aligned;
 };
 
+struct hammer_drvdata {
+	struct vivaldi_data vdata;
+	struct hammer_kbd_leds leds;
+};
+
 static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev,
 		enum led_brightness br)
 {
@@ -337,15 +344,11 @@ static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev,
 	return ret;
 }
 
-static int hammer_register_leds(struct hid_device *hdev)
+static int hammer_register_leds(struct hammer_drvdata *hdata, struct hid_device *hdev)
 {
 	struct hammer_kbd_leds *kbd_backlight;
-	int error;
-
-	kbd_backlight = kzalloc(sizeof(*kbd_backlight), GFP_KERNEL);
-	if (!kbd_backlight)
-		return -ENOMEM;
 
+	kbd_backlight = &hdata->leds;
 	kbd_backlight->hdev = hdev;
 	kbd_backlight->cdev.name = "hammer::kbd_backlight";
 	kbd_backlight->cdev.max_brightness = MAX_BRIGHTNESS;
@@ -356,26 +359,16 @@ static int hammer_register_leds(struct hid_device *hdev)
 	/* Set backlight to 0% initially. */
 	hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0);
 
-	error = led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
-	if (error)
-		goto err_free_mem;
-
-	hid_set_drvdata(hdev, kbd_backlight);
-	return 0;
-
-err_free_mem:
-	kfree(kbd_backlight);
-	return error;
+	return led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
 }
 
 static void hammer_unregister_leds(struct hid_device *hdev)
 {
-	struct hammer_kbd_leds *kbd_backlight = hid_get_drvdata(hdev);
+	struct hammer_drvdata *hdata = hid_get_drvdata(hdev);
+	struct hammer_kbd_leds *kbd_backlight = &hdata->leds;
 
-	if (kbd_backlight) {
+	if (kbd_backlight)
 		led_classdev_unregister(&kbd_backlight->cdev);
-		kfree(kbd_backlight);
-	}
 }
 
 #define HID_UP_GOOGLEVENDOR	0xffd10000
@@ -385,6 +378,58 @@ static void hammer_unregister_leds(struct hid_device *hdev)
 /* HID usage for keyboard backlight (Alphanumeric display brightness) */
 #define HID_AD_BRIGHTNESS	0x00140046
 
+static void hammer_feature_mapping(struct hid_device *hdev,
+				   struct hid_field *field,
+				   struct hid_usage *usage)
+{
+	struct hammer_drvdata *hdata = hid_get_drvdata(hdev);
+
+	vivaldi_hid_feature_mapping(&hdata->vdata, hdev, field, usage);
+}
+
+static ssize_t function_row_physmap_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct hid_device *hdev = to_hid_device(dev);
+	struct hammer_drvdata *hdata = hid_get_drvdata(hdev);
+	struct vivaldi_data *vdata = &hdata->vdata;
+
+	return vivaldi_function_row_physmap_show(vdata, buf);
+}
+
+static DEVICE_ATTR_RO(function_row_physmap);
+static struct attribute *hammer_sysfs_attrs[] = {
+	&dev_attr_function_row_physmap.attr,
+	NULL
+};
+
+static umode_t hammer_attr_is_visible(struct kobject *kobj,
+				      struct attribute *attr, int n)
+{
+	struct device *dev = kobj_to_dev(kobj);
+	struct hid_device *hdev = to_hid_device(dev);
+	struct hammer_drvdata *hdata = hid_get_drvdata(hdev);
+	struct vivaldi_data *vdata = &hdata->vdata;
+
+	if (attr == &dev_attr_function_row_physmap.attr &&
+	    !vdata->num_function_row_keys)
+		return 0;
+
+	return attr->mode;
+}
+
+static const struct attribute_group input_attribute_group = {
+	.is_visible = hammer_attr_is_visible,
+	.attrs = hammer_sysfs_attrs,
+};
+
+static int hammer_input_configured(struct hid_device *hdev,
+				   struct hid_input *hidinput)
+{
+	return sysfs_create_group(&hdev->dev.kobj, &input_attribute_group);
+}
+
 static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 				struct hid_field *field,
 				struct hid_usage *usage,
@@ -516,6 +561,12 @@ static int hammer_probe(struct hid_device *hdev,
 			const struct hid_device_id *id)
 {
 	int error;
+	struct hammer_drvdata *hdata;
+
+	hdata = devm_kzalloc(&hdev->dev, sizeof(*hdata), GFP_KERNEL);
+	if (!hdata)
+		return -ENOMEM;
+	hid_set_drvdata(hdev, hdata);
 
 	error = hid_parse(hdev);
 	if (error)
@@ -541,7 +592,7 @@ static int hammer_probe(struct hid_device *hdev,
 	}
 
 	if (hammer_has_backlight_control(hdev)) {
-		error = hammer_register_leds(hdev);
+		error = hammer_register_leds(hdata, hdev);
 		if (error)
 			hid_warn(hdev,
 				"Failed to register keyboard backlight: %d\n",
@@ -610,6 +661,8 @@ static struct hid_driver hammer_driver = {
 	.id_table = hammer_devices,
 	.probe = hammer_probe,
 	.remove = hammer_remove,
+	.feature_mapping = hammer_feature_mapping,
+	.input_configured = hammer_input_configured,
 	.input_mapping = hammer_input_mapping,
 	.event = hammer_event,
 };
