diff --git a/Documentation/ABI/testing/sysfs-class-led-trigger-tty b/Documentation/ABI/testing/sysfs-class-led-trigger-tty
index 2bf6b24e781b..14c38a2370ea 100644
--- a/Documentation/ABI/testing/sysfs-class-led-trigger-tty
+++ b/Documentation/ABI/testing/sysfs-class-led-trigger-tty
@@ -4,3 +4,57 @@ KernelVersion:	5.10
 Contact:	linux-leds@vger.kernel.org
 Description:
 		Specifies the tty device name of the triggering tty
+
+What:		/sys/class/leds/<led>/rx
+Date:		September 2023
+KernelVersion:	6.7
+Description:
+		Signal reception (rx) of data on the named tty device.
+		If set to 0, the LED will not blink on reception.
+		If set to 1 (default), the LED will blink on reception.
+
+What:		/sys/class/leds/<led>/tx
+Date:		September 2023
+KernelVersion:	6.7
+Description:
+		Signal transmission (tx) of data on the named tty device.
+		If set to 0, the LED will not blink on transmission.
+		If set to 1 (default), the LED will blink on transmission.
+
+car rng
+What:		/sys/class/leds/<led>/line_cts
+Date:		September 2023
+KernelVersion:	6.7
+Description:
+		DCE is ready to accept data from the DTE (Clear To Send). If
+		the line state is detected, the LED is switched on.
+		If set to 0 (default), the LED will not evaluate CTS.
+		If set to 1, the LED will evaluate CTS.
+
+What:		/sys/class/leds/<led>/line_dsr
+Date:		September 2023
+KernelVersion:	6.7
+Description:
+		DCE is ready to receive and send data (Data Set Ready). If
+		the line state is detected, the LED is switched on.
+		If set to 0 (default), the LED will not evaluate DSR.
+		If set to 1, the LED will evaluate DSR.
+
+What:		/sys/class/leds/<led>/line_car
+Date:		September 2023
+KernelVersion:	6.7
+Description:
+		DTE is receiving a carrier from the DCE (Data Carrier Detect).
+		If the line state is detected, the LED is switched on.
+		If set to 0 (default), the LED will not evaluate CAR (DCD).
+		If set to 1, the LED will evaluate CAR (DCD).
+
+What:		/sys/class/leds/<led>/line_cts
+Date:		September 2023
+KernelVersion:	6.7
+Description:
+		DCE has detected an incoming ring signal on the telephone
+		line (Ring Indicator). If the line state is detected, the
+		LED is switched on.
+		If set to 0 (default), the LED will not evaluate RNG (RI).
+		If set to 1, the LED will evaluate RNG (RI).
diff --git a/drivers/leds/trigger/ledtrig-tty.c b/drivers/leds/trigger/ledtrig-tty.c
index 8ae0d2d284af..0ea6d2a2599d 100644
--- a/drivers/leds/trigger/ledtrig-tty.c
+++ b/drivers/leds/trigger/ledtrig-tty.c
@@ -16,6 +16,28 @@ struct ledtrig_tty_data {
 	const char *ttyname;
 	struct tty_struct *tty;
 	int rx, tx;
+	unsigned long mode;
+#define LEDTRIG_TTY_MODE_TX	0
+#define LEDTRIG_TTY_MODE_RX	1
+#define LEDTRIG_TTY_MODE_CTS	2
+#define LEDTRIG_TTY_MODE_DSR	3
+#define LEDTRIG_TTY_MODE_CAR	4
+#define LEDTRIG_TTY_MODE_RNG	5
+};
+
+enum tty_led_state {
+	TTY_LED_BLINK,
+	TTY_LED_ENABLE,
+	TTY_LED_DISABLE,
+};
+
+enum ledtrig_tty_attr {
+	LEDTRIG_TTY_ATTR_TX,
+	LEDTRIG_TTY_ATTR_RX,
+	LEDTRIG_TTY_ATTR_CTS,
+	LEDTRIG_TTY_ATTR_DSR,
+	LEDTRIG_TTY_ATTR_CAR,
+	LEDTRIG_TTY_ATTR_RNG,
 };
 
 static void ledtrig_tty_restart(struct ledtrig_tty_data *trigger_data)
@@ -78,13 +100,184 @@ static ssize_t ttyname_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(ttyname);
 
+static ssize_t ledtrig_tty_attr_show(struct device *dev, char *buf,
+	enum ledtrig_tty_attr attr)
+{
+	struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
+	int bit;
+
+	switch (attr) {
+	case LEDTRIG_TTY_ATTR_TX:
+		bit = LEDTRIG_TTY_MODE_TX;
+		break;
+	case LEDTRIG_TTY_ATTR_RX:
+		bit = LEDTRIG_TTY_MODE_RX;
+		break;
+	case LEDTRIG_TTY_ATTR_CTS:
+		bit = LEDTRIG_TTY_MODE_CTS;
+		break;
+	case LEDTRIG_TTY_ATTR_DSR:
+		bit = LEDTRIG_TTY_MODE_DSR;
+		break;
+	case LEDTRIG_TTY_ATTR_CAR:
+		bit = LEDTRIG_TTY_MODE_CAR;
+		break;
+	case LEDTRIG_TTY_ATTR_RNG:
+		bit = LEDTRIG_TTY_MODE_RNG;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));
+}
+
+static ssize_t ledtrig_tty_attr_store(struct device *dev, const char *buf,
+	size_t size, enum ledtrig_tty_attr attr)
+{
+	struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
+	unsigned long state;
+	int ret;
+	int bit;
+
+	ret = kstrtoul(buf, 0, &state);
+	if (ret)
+		return ret;
+
+	switch (attr) {
+	case LEDTRIG_TTY_ATTR_TX:
+		bit = LEDTRIG_TTY_MODE_TX;
+		break;
+	case LEDTRIG_TTY_ATTR_RX:
+		bit = LEDTRIG_TTY_MODE_RX;
+		break;
+	case LEDTRIG_TTY_ATTR_CTS:
+		bit = LEDTRIG_TTY_MODE_CTS;
+		break;
+	case LEDTRIG_TTY_ATTR_DSR:
+		bit = LEDTRIG_TTY_MODE_DSR;
+		break;
+	case LEDTRIG_TTY_ATTR_CAR:
+		bit = LEDTRIG_TTY_MODE_CAR;
+		break;
+	case LEDTRIG_TTY_ATTR_RNG:
+		bit = LEDTRIG_TTY_MODE_RNG;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (state)
+		set_bit(bit, &trigger_data->mode);
+	else
+		clear_bit(bit, &trigger_data->mode);
+
+	return size;
+}
+
+static ssize_t tx_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return ledtrig_tty_attr_show(dev, buf, LEDTRIG_TTY_ATTR_TX);
+}
+
+static ssize_t tx_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	return ledtrig_tty_attr_store(dev, buf, size, LEDTRIG_TTY_ATTR_TX);
+}
+static DEVICE_ATTR_RW(tx);
+
+static ssize_t rx_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return ledtrig_tty_attr_show(dev, buf, LEDTRIG_TTY_ATTR_RX);
+}
+
+static ssize_t rx_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	return ledtrig_tty_attr_store(dev, buf, size, LEDTRIG_TTY_ATTR_RX);
+}
+static DEVICE_ATTR_RW(rx);
+
+static ssize_t line_cts_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return ledtrig_tty_attr_show(dev, buf, LEDTRIG_TTY_ATTR_CTS);
+}
+
+static ssize_t line_cts_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	return ledtrig_tty_attr_store(dev, buf, size, LEDTRIG_TTY_ATTR_CTS);
+}
+static DEVICE_ATTR_RW(line_cts);
+
+static ssize_t line_dsr_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return ledtrig_tty_attr_show(dev, buf, LEDTRIG_TTY_ATTR_DSR);
+}
+
+static ssize_t line_dsr_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	return ledtrig_tty_attr_store(dev, buf, size, LEDTRIG_TTY_ATTR_DSR);
+}
+static DEVICE_ATTR_RW(line_dsr);
+
+static ssize_t line_car_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return ledtrig_tty_attr_show(dev, buf, LEDTRIG_TTY_ATTR_CAR);
+}
+
+static ssize_t line_car_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	return ledtrig_tty_attr_store(dev, buf, size, LEDTRIG_TTY_ATTR_CAR);
+}
+static DEVICE_ATTR_RW(line_car);
+
+static ssize_t line_rng_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return ledtrig_tty_attr_show(dev, buf, LEDTRIG_TTY_ATTR_RNG);
+}
+
+static ssize_t line_rng_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	return ledtrig_tty_attr_store(dev, buf, size, LEDTRIG_TTY_ATTR_RNG);
+}
+static DEVICE_ATTR_RW(line_rng);
+
+
+static int ledtrig_tty_flag(struct ledtrig_tty_data *trigger_data, unsigned int flag)
+{
+	unsigned int status;
+	int ret;
+
+	status = tty_get_mget(trigger_data->tty);
+	if (status & flag)
+		ret = 1;
+	else
+		ret = 0;
+
+	return ret;
+}
+
 static void ledtrig_tty_work(struct work_struct *work)
 {
 	struct ledtrig_tty_data *trigger_data =
 		container_of(work, struct ledtrig_tty_data, dwork.work);
+	struct led_classdev *led_cdev = trigger_data->led_cdev;
 	struct serial_icounter_struct icount;
+	enum tty_led_state state;
 	int ret;
 
+	state = TTY_LED_DISABLE;
 	mutex_lock(&trigger_data->mutex);
 
 	if (!trigger_data->ttyname) {
@@ -115,22 +308,69 @@ static void ledtrig_tty_work(struct work_struct *work)
 		trigger_data->tty = tty;
 	}
 
-	ret = tty_get_icount(trigger_data->tty, &icount);
-	if (ret) {
-		dev_info(trigger_data->tty->dev, "Failed to get icount, stopped polling\n");
-		mutex_unlock(&trigger_data->mutex);
-		return;
+	if (test_bit(LEDTRIG_TTY_MODE_CTS, &trigger_data->mode)) {
+		ret = ledtrig_tty_flag(trigger_data, TIOCM_CTS);
+		if (ret)
+			state = TTY_LED_ENABLE;
 	}
 
-	if (icount.rx != trigger_data->rx ||
-	    icount.tx != trigger_data->tx) {
-		unsigned long interval = LEDTRIG_TTY_INTERVAL;
+	if (test_bit(LEDTRIG_TTY_MODE_DSR, &trigger_data->mode)) {
+		ret = ledtrig_tty_flag(trigger_data, TIOCM_DSR);
+		if (ret)
+			state = TTY_LED_ENABLE;
+	}
 
+	if (test_bit(LEDTRIG_TTY_MODE_CAR, &trigger_data->mode)) {
+		ret = ledtrig_tty_flag(trigger_data, TIOCM_CAR);
+		if (ret)
+			state = TTY_LED_ENABLE;
+	}
+
+	if (test_bit(LEDTRIG_TTY_MODE_RNG, &trigger_data->mode)) {
+		ret = ledtrig_tty_flag(trigger_data, TIOCM_RNG);
+		if (ret)
+			state = TTY_LED_ENABLE;
+	}
+
+	/* The rx/tx handling must come after the evaluation of TIOCM_*,
+	 * since the display for rx/tx has priority
+	 */
+	if (test_bit(LEDTRIG_TTY_MODE_TX, &trigger_data->mode) ||
+	    test_bit(LEDTRIG_TTY_MODE_RX, &trigger_data->mode)) {
+		ret = tty_get_icount(trigger_data->tty, &icount);
+		if (ret) {
+			dev_info(trigger_data->tty->dev, "Failed to get icount, stopped polling\n");
+			mutex_unlock(&trigger_data->mutex);
+			return;
+		}
+
+		if (test_bit(LEDTRIG_TTY_MODE_TX, &trigger_data->mode) &&
+		    (icount.tx != trigger_data->tx)) {
+			trigger_data->tx = icount.tx;
+			state = TTY_LED_BLINK;
+		}
+
+		if (test_bit(LEDTRIG_TTY_MODE_RX, &trigger_data->mode) &&
+		    (icount.rx != trigger_data->rx)) {
+			trigger_data->rx = icount.rx;
+			state = TTY_LED_BLINK;
+		}
+	}
+
+	switch (state) {
+	case TTY_LED_BLINK:
+		unsigned long interval = LEDTRIG_TTY_INTERVAL;
 		led_blink_set_oneshot(trigger_data->led_cdev, &interval,
 				      &interval, 0);
-
-		trigger_data->rx = icount.rx;
-		trigger_data->tx = icount.tx;
+		break;
+	case TTY_LED_ENABLE:
+		led_set_brightness(led_cdev, led_cdev->blink_brightness);
+		break;
+	case TTY_LED_DISABLE:
+		fallthrough;
+	default:
+		led_set_brightness(led_cdev, 0);
+		break;
 	}
 
 out:
@@ -141,6 +381,12 @@ static void ledtrig_tty_work(struct work_struct *work)
 
 static struct attribute *ledtrig_tty_attrs[] = {
 	&dev_attr_ttyname.attr,
+	&dev_attr_rx.attr,
+	&dev_attr_tx.attr,
+	&dev_attr_line_cts.attr,
+	&dev_attr_line_dsr.attr,
+	&dev_attr_line_car.attr,
+	&dev_attr_line_rng.attr,
 	NULL
 };
 ATTRIBUTE_GROUPS(ledtrig_tty);
@@ -153,6 +399,10 @@ static int ledtrig_tty_activate(struct led_classdev *led_cdev)
 	if (!trigger_data)
 		return -ENOMEM;
 
+	/* Enable default rx/tx LED blink */
+	set_bit(LEDTRIG_TTY_MODE_TX, &trigger_data->mode);
+	set_bit(LEDTRIG_TTY_MODE_RX, &trigger_data->mode);
+
 	led_set_trigger_data(led_cdev, trigger_data);
 
 	INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_tty_work);
