162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Samsung LSI S5C73M3 8M pixel camera driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012, Samsung Electronics, Co., Ltd.
662306a36Sopenharmony_ci * Sylwester Nawrocki <s.nawrocki@samsung.com>
762306a36Sopenharmony_ci * Andrzej Hajda <a.hajda@samsung.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/sizes.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/media.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/spi/spi.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "s5c73m3.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic const struct of_device_id s5c73m3_spi_ids[] = {
2362306a36Sopenharmony_ci	{ .compatible = "samsung,s5c73m3" },
2462306a36Sopenharmony_ci	{ }
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, s5c73m3_spi_ids);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cienum spi_direction {
2962306a36Sopenharmony_ci	SPI_DIR_RX,
3062306a36Sopenharmony_ci	SPI_DIR_TX
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int spi_xmit(struct spi_device *spi_dev, void *addr, const int len,
3462306a36Sopenharmony_ci							enum spi_direction dir)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct spi_message msg;
3762306a36Sopenharmony_ci	int r;
3862306a36Sopenharmony_ci	struct spi_transfer xfer = {
3962306a36Sopenharmony_ci		.len	= len,
4062306a36Sopenharmony_ci	};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (dir == SPI_DIR_TX)
4362306a36Sopenharmony_ci		xfer.tx_buf = addr;
4462306a36Sopenharmony_ci	else
4562306a36Sopenharmony_ci		xfer.rx_buf = addr;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (spi_dev == NULL) {
4862306a36Sopenharmony_ci		pr_err("SPI device is uninitialized\n");
4962306a36Sopenharmony_ci		return -ENODEV;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	spi_message_init(&msg);
5362306a36Sopenharmony_ci	spi_message_add_tail(&xfer, &msg);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	r = spi_sync(spi_dev, &msg);
5662306a36Sopenharmony_ci	if (r < 0)
5762306a36Sopenharmony_ci		dev_err(&spi_dev->dev, "%s spi_sync failed %d\n", __func__, r);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return r;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciint s5c73m3_spi_write(struct s5c73m3 *state, const void *addr,
6362306a36Sopenharmony_ci		      const unsigned int len, const unsigned int tx_size)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct spi_device *spi_dev = state->spi_dev;
6662306a36Sopenharmony_ci	u32 count = len / tx_size;
6762306a36Sopenharmony_ci	u32 extra = len % tx_size;
6862306a36Sopenharmony_ci	unsigned int i, j = 0;
6962306a36Sopenharmony_ci	u8 padding[32];
7062306a36Sopenharmony_ci	int r = 0;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	memset(padding, 0, sizeof(padding));
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
7562306a36Sopenharmony_ci		r = spi_xmit(spi_dev, (void *)addr + j, tx_size, SPI_DIR_TX);
7662306a36Sopenharmony_ci		if (r < 0)
7762306a36Sopenharmony_ci			return r;
7862306a36Sopenharmony_ci		j += tx_size;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (extra > 0) {
8262306a36Sopenharmony_ci		r = spi_xmit(spi_dev, (void *)addr + j, extra, SPI_DIR_TX);
8362306a36Sopenharmony_ci		if (r < 0)
8462306a36Sopenharmony_ci			return r;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return spi_xmit(spi_dev, padding, sizeof(padding), SPI_DIR_TX);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciint s5c73m3_spi_read(struct s5c73m3 *state, void *addr,
9162306a36Sopenharmony_ci		     const unsigned int len, const unsigned int tx_size)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct spi_device *spi_dev = state->spi_dev;
9462306a36Sopenharmony_ci	u32 count = len / tx_size;
9562306a36Sopenharmony_ci	u32 extra = len % tx_size;
9662306a36Sopenharmony_ci	unsigned int i, j = 0;
9762306a36Sopenharmony_ci	int r = 0;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
10062306a36Sopenharmony_ci		r = spi_xmit(spi_dev, addr + j, tx_size, SPI_DIR_RX);
10162306a36Sopenharmony_ci		if (r < 0)
10262306a36Sopenharmony_ci			return r;
10362306a36Sopenharmony_ci		j += tx_size;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (extra > 0)
10762306a36Sopenharmony_ci		return spi_xmit(spi_dev, addr + j, extra, SPI_DIR_RX);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int s5c73m3_spi_probe(struct spi_device *spi)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	int r;
11562306a36Sopenharmony_ci	struct s5c73m3 *state = container_of(spi->dev.driver, struct s5c73m3,
11662306a36Sopenharmony_ci					     spidrv.driver);
11762306a36Sopenharmony_ci	spi->bits_per_word = 32;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	r = spi_setup(spi);
12062306a36Sopenharmony_ci	if (r < 0) {
12162306a36Sopenharmony_ci		dev_err(&spi->dev, "spi_setup() failed\n");
12262306a36Sopenharmony_ci		return r;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	mutex_lock(&state->lock);
12662306a36Sopenharmony_ci	state->spi_dev = spi;
12762306a36Sopenharmony_ci	mutex_unlock(&state->lock);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	v4l2_info(&state->sensor_sd, "S5C73M3 SPI probed successfully\n");
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciint s5c73m3_register_spi_driver(struct s5c73m3 *state)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct spi_driver *spidrv = &state->spidrv;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	spidrv->probe = s5c73m3_spi_probe;
13862306a36Sopenharmony_ci	spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
13962306a36Sopenharmony_ci	spidrv->driver.of_match_table = s5c73m3_spi_ids;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return spi_register_driver(spidrv);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_civoid s5c73m3_unregister_spi_driver(struct s5c73m3 *state)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	spi_unregister_driver(&state->spidrv);
14762306a36Sopenharmony_ci}
148